PHP double sort array based on substring - php

I am building a custom switch manager for work, my current issue is more an aesthetic one but I think it is a good learning experience. I have posted the array below for clarity:
Array
(
[1] => FastEthernet0/1
[10] => FastEthernet0/10
[11] => FastEthernet0/11
[12] => FastEthernet0/12
[13] => FastEthernet0/13
[14] => FastEthernet0/14
[15] => FastEthernet0/15
[16] => FastEthernet0/16
[17] => FastEthernet0/17
[18] => FastEthernet0/18
[19] => FastEthernet0/19
[2] => FastEthernet0/2
[20] => FastEthernet0/20
[21] => FastEthernet0/21
[22] => FastEthernet0/22
[23] => FastEthernet0/23
[24] => FastEthernet0/24
[3] => FastEthernet0/3
[4] => FastEthernet0/4
[5] => FastEthernet0/5
[6] => FastEthernet0/6
[7] => FastEthernet0/7
[8] => FastEthernet0/8
[9] => FastEthernet0/9
[25] => Null0
)
On our bigger switches I am using asort($arr); to get GigabitEthernet1/1 to come before 2/1, etc...
My goal is to sort on the interface number (part after '/') so that 1/8 comes before 1/10.
Could someone point me in the right direction, I want to work for the results but I am not familiar enough with PHP to know exactly where to go.
Notes: On out larger multi-module switches the IDs are not in order so a sort on $arr[key] won't work.

You can use the flag while using asort(), like below.
asort($arr, SORT_NATURAL | SORT_FLAG_CASE);print_r($arr);
It will print/sort the data as yo need.

The SORT_NATURAL and SORT_FLAG_CASE requires v5.4+.
If you're using an older version of PHP, you could do it with uasort and a custom comparison callback function.
$interfaces = array(...);
$ifmaj = array();
$ifmin = array();
$if_cmp = function ($a, $b) {
list($amaj,$amin) = split('/',$a);
list($bmaj,$bmin) = split('/',$b);
$maj = strcmp($amaj,$bmaj);
if ($maj!=0) return $maj;
//Assuming right side is an int
return $amin-$bmin;
};
uasort($interfaces, $if_cmp);

Related

How to split 1 array into 2 arrays, remove certain items, and combine them again into 1 array in PHP?

i want to create something using array. I have 1 array and i need to split it into 2 array. After that search specific items from both array and remove it then combine it 2 array into 1 array.
How do i do that?
I already try to use unset for array but confuse how to use it for specific key since my array data format like 16/2/1/1 and 16/2/1/5. I need to remove data which have 1.
My format array is like this
Array
(
[1] => Array
(
[0] => 16/2/1/1 --> remove this have 1 after 2
[1] => 16/2/0/2
[2] => 16/2/0/3
[3] => 16/2/0/4
[4] => 16/2/0/5
[5] => 16/2/0/6
[6] => 16/2/0/7
[7] => 16/2/0/8
[8] => 16/2/0/9
[9] => 16/2/0/10
[10] => 16/2/0/11
[11] => 16/2/0/12
[12] => 16/2/0/13
[13] => 16/2/0/14
[14] => 16/2/0/15
[15] => 16/2/0/16
)
[2] => Array
(
[0] => 16/2/0/1
[1] => 16/2/0/2
[2] => 16/2/0/3
[3] => 16/2/0/4
[4] => 16/2/1/5 --> and this have 1 after 2 before 5
[5] => 16/2/0/6
[6] => 16/2/0/7
[7] => 16/2/0/8
[8] => 16/2/0/9
[9] => 16/2/0/10
[10] => 16/2/0/11
[11] => 16/2/0/12
[12] => 16/2/0/13
[13] => 16/2/0/14
[14] => 16/2/0/15
[15] => 16/2/0/16
)
)
i expect the output something like (after combine)
Array
(
[0] => 16/2/0/2
[1] => 16/2/0/3
[2] => 16/2/0/4
[3] => 16/2/0/6
[4] => 16/2/0/7
[5] => 16/2/0/8
[6] => 16/2/0/9
[7] => 16/2/0/10
[8] => 16/2/0/11
[9] => 16/2/0/12
[10] => 16/2/0/13
[11] => 16/2/0/14
[12] => 16/2/0/15
[13] => 16/2/0/16
)
Thanks for time to help me.
Make the array unique and then extract items that are digits/digits/NOT 1/digits:
$array = preg_grep('#^\d+/\d+/[^1]/\d+#', array_unique($array));
I would use preg_grep which allows you to search an array using a Regular expression.
$array =[
'16/2/0/13',
'16/2/0/16',
'16/2/1/5'
];
$array = preg_grep('~^16/2/0/\d+$~', $array);
print_r($array);
Output
Array
(
[0] => 16/2/0/13
[1] => 16/2/0/16
)
Sandbox
The Regex
^ match start of string
16/2/0/ - match literally (at the start of string, see above)
\d+ any digit one or more
$ match end of string
So Regular expressions is a way to do pattern matching, in this case the pattern is 16/2/0/{n} where {n} is any number. So by doing this we can find only those items that match that pattern.
Then if you have duplicates, you can do array_unique() and easily remove those.
There are many ways to do this array_filter with a custom callback etc. But this is the most straightforward way (if you know Regex).

find non duplicated item in an array

I have two arrays built from different directories that contain file names stripped of extensions. I want to find the ones that don't make a pair thus I merged the array to obtain the array below. How can I find the only non duplicate item in an array?
Array
(
[0] => dbbackup_2014.09.03_07_06_27
[1] => dbbackup_2014.09.03_07_07_08
[2] => dbbackup_2014.09.03_07_13_33
[3] => dbbackup_2014.09.03_07_15_24
[4] => dbbackup_2014.09.03_07_21_57
[5] => dbbackup_2014.09.03_07_22_11
[6] => dbbackup_2014.09.03_08_40_35
[7] => dbbackup_2014.09.03_08_41_36
[8] => dbbackup_2014.09.03_08_43_38
[9] => dbbackup_2014.09.04_04_59_08
[10] => dbbackup_2014.09.03_07_06_27
[11] => dbbackup_2014.09.03_07_07_08
[12] => dbbackup_2014.09.03_07_13_33
[13] => dbbackup_2014.09.03_07_15_24
[14] => dbbackup_2014.09.03_07_21_57
[15] => dbbackup_2014.09.03_07_22_11
[16] => dbbackup_2014.09.03_08_40_35
[17] => dbbackup_2014.09.03_08_41_36
[18] => dbbackup_2014.09.03_08_43_38
)
Note: it is [9]
$a = array_flip(array_filter(array_count_values($a),function($item){
return $item == 1 ? true : false;
}));
print_r($a);
Output
Array
(
[1] => dbbackup_2014.09.04_04_59_08
)
Ideone
foreach($array as $data)
{
$values=explode("_",$data);
$output[$values[1]]++;
}
foreach($output as $date=>$number)
{
if($number==1)
echo $date;
}
Output:
2014.09.04
Fiddle

Sort array not working

I am trying to sort array alphabetically using php sort function.
Array ( [0] => Open Sans [1] => Wellfleet [2] => Rambla [3] => Dosis [4] => Noto Sans [5] => Domine [6] => Signika Negative [7] => Arvo [8] => Neuton [9] => Rufina [10] => Tinos [11] => Podkova [12] => Magra [13] => Bitter [14] => Anton [15] => Libre Baskerville [16] => Tienne [17] => Roboto [18] => Ruda [19] => Merriweather [20] => Amaranth [21] => Playfair Display SC [22] => Cinzel Decorative [23] => Nobile [24] => Volkhov [25] => Nunito [26] => Merriweather Sans [27] => Stardos Stencil [28] => Bree Serif )
I have tried this one
$heading_fonts = sort($heading_fonts);
Eventually I am combining array to get same key and value.
$heading_fonts = array_combine($heading_fonts, $heading_fonts);
But giving me an error.
Warning: array_combine() expects parameter 1 to be array, boolean given in...
Any idea how can I sort an array to work?
The sort function will sort your array in place and return a boolean value indicating its success. You should not assign its return value to the array. Use just:
sort($heading_fonts);
Learn to use the documentation:
bool sort ( array &$array [, int $sort_flags = SORT_REGULAR ] )
Notice the ampersand (&) in-front of the $array parameter. The ampersand is a reference operator.
It is also important to always take note of a function's return value. Many questions here on SO can be solved simply by finding out what a function actually does, and what it returns.
Return Values
Returns TRUE on success or FALSE on failure.
So, this is how you sort an array:
$someArray = array(8,2,6,1,0);
sort($someArray);
// $someArray is now sorted
If you assign the return value to $someArray, like this:
$someArray = array(8,2,6,1,0);
$someArray = sort($someArray);
...then $someArray is overwritten by the sort() function's return value (which would be TRUE in this case).
The "Examples" section of the documentation is also very instructive.

UCA + Natural Sorting

I recently learnt that PHP already supports the Unicode Collation Algorithm via the intl extension:
$array = array
(
'al', 'be',
'Alpha', 'Beta',
'Álpha', 'Àlpha', 'Älpha',
'かたかな',
'img10.png', 'img12.png',
'img1.png', 'img2.png',
);
if (extension_loaded('intl') === true)
{
collator_asort(collator_create('root'), $array);
}
Array
(
[0] => al
[2] => Alpha
[4] => Álpha
[5] => Àlpha
[6] => Älpha
[1] => be
[3] => Beta
[11] => img1.png
[9] => img10.png
[8] => img12.png
[10] => img2.png
[7] => かたかな
)
As you can see this seems to work perfectly, even with mixed case strings! The only drawback I've encountered so far is that there is no support for natural sorting and I'm wondering what would be the best way to work around that, so that I can merge the best of the two worlds.
I've tried to specify the Collator::SORT_NUMERIC sort flag but the result is way messier:
collator_asort(collator_create('root'), $array, Collator::SORT_NUMERIC);
Array
(
[8] => img12.png
[7] => かたかな
[9] => img10.png
[10] => img2.png
[11] => img1.png
[6] => Älpha
[5] => Àlpha
[1] => be
[2] => Alpha
[3] => Beta
[4] => Álpha
[0] => al
)
However, if I run the same test with only the img*.png values I get the ideal output:
Array
(
[3] => img1.png
[2] => img2.png
[1] => img10.png
[0] => img12.png
)
Can anyone think of a way to preserve the Unicode sorting while adding natural sorting capabilities?
After digging a little more in the documentation I've found the solution:
if (extension_loaded('intl') === true)
{
if (is_object($collator = collator_create('root')) === true)
{
$collator->setAttribute(Collator::NUMERIC_COLLATION, Collator::ON);
$collator->asort($array);
}
}
Output:
Array
(
[0] => al
[3] => Alpha
[5] => Álpha
[6] => Àlpha
[7] => Älpha
[1] => be
[4] => Beta
[10] => img1.png
[11] => img2.png
[8] => img10.png
[9] => img12.png
[2] => かたかな
)
This is trivially done. You simply preprocess the list to zero-pad numbers. For example, using my ucsort script, which supports the UCA, on this list of filenames:
% cat /tmp/numfiles
img4.png
img1.png
img2.png
img12.png
img21.png
img10.png
img20.png
img3.png
img22.png
will produce the desired output by using the Unicode::Collate module’s --preprocess hook to transform runs of digits into zero-padded ones:
% ucsort --preprocess='s/(\d+)/sprintf "%020d", $1/ge' /tmp/numfiles
img1.png
img2.png
img3.png
img4.png
img10.png
img12.png
img20.png
img21.png
img22.png
Looking at the PHP documentation you cite, it does not appear that that PHP library supports the full UCA tailoring possibilities that the Perl Unicode::Collate module supports. In fact, it looks more like Perl’s Unicode::Collate::Locale module, except that the PHP library code does not seem to support the inherited collation options that the Perl code does.
I suppose that if all else fails, you could call Perl code to do what needs done.
Based on the answer of #tchrist I've came up with this:
function sortIntl($array, $natural = true)
{
$data = $array;
if ($natural === true)
{
$data = preg_replace_callback('~([0-9]+)~', 'natsortIntl', $data);
}
collator_asort(collator_create('root'), $data);
return array_intersect_key($array, $data);
}
function natsortIntl($number)
{
return sprintf('%020d', $number);
}
Output:
Array
(
[0] => 1
[1] => 100
[2] => al
[3] => be
[4] => Alpha
[5] => Beta
[6] => Álpha
[7] => Àlpha
[8] => Älpha
[9] => かたかな
[10] => img1.png
[11] => img2.png
[12] => img10.png
[13] => img20.png
)
Still hoping for a better solution though.

serializing an array and storing in DB

i have an array like
$newArray = $_POST[$newId];
print_r($newArray);
it prints like
Array ( [1] => Yes [2] => a [3] => b [4] => c [5] => d [6] => e [7] => f [8] => [9] => [10] => [11] => [12] => [13] => [14] => )
but when i try to store in in db after serializing like
serialize($newArray)
it get stored like
s:211:"Array
(
[1] => Yes
[2] => ab
[3] => c
[4] => d
[5] => e
[6] => f
[7] =>
[8] =>
[9] =>
[10] =>
[11] =>
[12] =>
[13] =>
[14] =>
)
";
which is a single array element in DB..how do i properly serialize the element.
It looks like it's serializing a string, not an array. Are you sure $newArray is an array?
The string returned from serialize starts with 's:211'. This means that a string was passed into serialize(). If an array were passed into serialize() the returned string would start with 'a:14'.
#pradeep
where you storing $newArray in textfield, store it by serialling
$arrayString = $_POST['newId'];
You will get seriallized array in $arrayString.
If you want to use that array before storing in database , use unserialize , else directly store in database as that is already seriallized.
$array = unserialize($arrayString);
This will solve your problem
Not really sure I understand the question, if you don't want to serialize though, and if you want to pass it from a text field, maybe do custom syntax like - 1:a;b:2;c:3
then explode(';',$string); loop that and for the result, explode(':',$rows)
make the delimiters more difficult to clash
explode("[[;]]", string); // 1]]:[[b[[;]]

Categories