Stable sort array using second dimension key - php

I have to use a webservice which return JSON. After decode JSON I get array:
$arrays[0]["2013-04-09"]=$someValue;
$arrays[1]["2013-04-09"]=$someValue;
$arrays[2]["2013-04-11"]=$someValue;
$arrays[3]["2013-04-05"]=$someValue;
$arrays[4]["2013-04-09"]=$someValue;
I want sort (stable way and using key of second dim key) array and get as result:
$arrays[3]["2013-04-05"];
$arrays[0]["2013-04-09"]; //stable way don't swap with next val
$arrays[1]["2013-04-09"]; //stable way don't swap with next and prev vel
$arrays[4]["2013-04-09"]; //stable way, don't swap with prev val
$arrays[2]["2013-04-11"];
Can you help me? I try create own function of sort beacause ksort or krsort sort using only first dim key. Thank you for answers.
EDIT:
I try write my own function - and this works - I got wrong "valid answers" in my units test and this is reason that I said that this isn't works:
private function getResult(){
...
usort($arrays,array($this,'mycmp'));
...
}
private function mycmp($a, $b){
foreach($a as $key=>$val){
$first = $key;
}
foreach($b as $key=>$val){
$second = $key;
}
if ($first == $second){
return 0;
}
return ($first < $second) ? -1:1;
}
THANKS FOR HELP

Assuming that each of the sub-arrays has the date key in the same place (e.g. always first key in the object), you could do the following:
$keyPosition = 0; // location of the key in the array structures
usort($data, function ($a, $b) use ($keyPosition) {
$aKeys = array_keys($a);
$bKeys = array_keys($b);
return strcmp($aKeys[$keyPosition], $bKeys[$keyPosition]);
});
However, what you should be aware is that PHP no longer supports stable sorting, so you may not get exactly the same ordering as previous. If this is an absolute requirement, you may need to roll your own sort function. Here's a Gist that might help.

Related

PHP - Elegantly extract the numeric indices in array a that are not in array b (not array_diff_key)

Suppose you have two arrays $a=array('apple','banana','canaple'); and $b=array('apple');, how do you (elegantly) extract the numeric indices of elements in array a that aren't in array b? (in this case, indices: 1 and 2).
In this case, array a will always have more elements than b.
Note, this is not asking for array_diff_key, but rather the numeric indices in the array with more elements that don't exist in the array with fewer elements.
array_diff gets you half way there. Using array_keys on the diff gets you the rest of what you want.
$a = ['apple','banana','canaple'];
$b = ['apple'];
$diff = array_diff($a, $b);
$keys = array_keys($diff);
var_dump($keys); // [1, 2]
This is because array_diff returns both the element and it's key from the first array. If you wanted to write a PHP implementation of array_diff it might look something like this...
function array_diff(Array ... $arrays) {
$return = [];
$cmp = array_shift($arrays);
foreach ($cmp as $key => $value) {
foreach($arrays as $array) {
if (!in_array($value, $array)) {
$return[$key] = $value;
}
}
}
return $return;
}
This gives you an idea how you might achieve the result, but internally php implements this as a sort, because it's much faster than the aforementioned implementation.

Reducing a multi-dimensional array

I have an array that carries a definite number of dimensions so I'm not really looking at something recursive (Unless maybe for maintainability sake in the future). It's a numeric array gotten from the database with each row holding another array. Each of those level 2 arrays contain strings like
var1, var2 , var3
And so on. Note the irregular appearance of commas in the string. So I intend to break the comma delimited string in the third level then log them in the final array but I get an error saying I am supplying an null array. So I want to know why it says the array is null and how I can make it recognise that as a valid array. My code goes below:
function fetch_each($arr) {
$temp = array();
for ($i = 0; $i < count($arr); $i++) {
for ($j = 0; $j < count($arr[$i]); $j++) {
array_reduce(preg_split("/[\s,]+/", $arr[$i][$j]), function($a, $b) {
return array_push($temp, $a, $b);
});
}
}
return $temp;
}
PS: Please don't mark as duplicate. I don't want to copy someone else's code but want to understand why this does not work. Thanks.
You have this problem because $temp is not visible in the function block.
To solve that, you must use the keyword use (variable_name) next to the function definition as in this example :
array_reduce(preg_split("/[\s,]+/", $arr[$i][$j]), function($a, $b) use (&$temp) {
return array_push($temp, $a, $b);
});
Just a remark, $a will contain the result of array_push
Returns:int the new number of elements in the array.
So you can remove it from the array_push() instruction to keep a clean array with only splitted strings

Find the most recent date from a nested array

I have a Multilevel Array (See array structure picture below) and I need to get the sub-nested array with the higher date value.
I was wondering if there is a straight forward way to sort sub-nested arrays by date value or get the highest date value?
Array Map
To do that, usort() function will be useful:
usort($rgData, function($rgX, $rgY)
{
$x = strtotime($rgX['date']);
$y = strtotime($rgY['date']);
return $x<$y?-1:$x!=$y;
});
//var_dump($rgData);
if you want to get highest value, then it will be ['date'] key of last element after doing the sort above.
Edit: if you're sure that format will be exactly same as on picture always, you can use direct string comparison via strcmp (that would be probably faster)
How about using usort():
$input = array(
array('date' => '2013-09-11 13:08:40 +0000'),
array('date' => '2013-09-11 13:09:17 +0000'));
usort($input, function(array $a, array $b) {
$aTimestamp = strtotime($a['date']);
$bTimestamp = strtotime($b['date']);
if($aTimestamp == $bTimestamp) return 0;
return $aTimestamp < $bTimestamp;
});
print_r($input); //$input[0] has the latest date value

Using array_multisort() and putting empty values last, not first

I have 4 arrays and use array_multisort to sort them all at the same time relative to one another.
Problem is, in the first array, there can be empty values and I want to put them at the end, not the beginning.
Example : http://codepad.org/V6TjCsS5
Is there a way to:
Pass a custom function to array_multisort
or
Sort the first array with a custom function then use the result order to sort the other arrays
or
Use a certain argument with array_multisort to achieve what I want
Thank you very much
Unfortunately none of the approaches your propose is possible, which means you have to take another step back and look for alternatives. I am assuming you want a normal ascending sort, with the explicit exception that empty elements (which you be "smallest") need to be considered as "largest".
Option 1: Manually rearrange elements after sorting
Do your array_multisort as usual, and then make the modifications you require:
// $arr1, $arr2 etc have been sorted with array_multisort
while(reset($arr1) == '') {
$k = key($arr1);
unset($arr1[$k]); // remove empty element from beginning of array
$arr1[$k] = ''; // add it to end of array
// and now do the same for $arr2
$v = reset($arr2);
$k = key($arr2);
unset($arr2[$k]);
$arr2[$k] = $v;
// the same for $arr3, etc
}
You can pull out part of the code in a function to make this prettier:
function shift_and_push(&$arr) {
$v = reset($arr);
$k = key($arr);
unset($arr[$k]);
$arr[$k] = $v;
}
Option 2: Condense everything inside one array so you can use usort
The idea here is to pull all your arrays into one so you can specify the comparison function by using usort:
$allArrays = array_map(function() { return func_get_args(); },
$array1, $array2 /* , as many arrays as you want */);
You can now sort:
// writing this as a free function so that it looks presentable
function cmp($row1, $row2) {
// $row1[0] is the item in your first array, etc
if($row1[0] == $row2[0]) {
return 0;
}
else if($row1[0] == '') {
return 1;
}
else if($row2[0] == '') {
return -1;
}
return $row1[0] < $row2[0] ? -1 : 1;
}
usort($allArrays, "cmp");
At this point you are left with an array, each element (row) of which is an array. The first elements of each are what was originally inside $array1, second elements are what was in $array2, etc. By placing those elements inside "rows", we have managed to keep the sort order among all your original arrays synchronized.
The second argument to array_multisort can be the options to the sort. So you could pass SORT_DESC if SORT_ASC is doing the opposite of what you want.
array_multisort($arr, SORT_DESC);
For completeness sake, here are the other options SORT_ASC, SORT_DESC, SORT_REGULAR, SORT_NUMERIC, SORT_STRING. SORT_STRING may be helpful.
Do you actually want the empty elements? Just remove them before sorting:
foreach($array1 as &$v) {
if($v==='')
{
unset($v);
}
}

PHP sort 2d array by index (non-associative)

This code does not run properly, but it suggests what I am trying to do:
function sort_2d_by_index($a,$i) {
function cmp($x, $y) {
// Nested function, can't find $i
// (global $i defeats the purpose of passing an arg)
if ($x[$i] == $y[$i]) { return 0; }
return ($x[$i] < $y[$i]) ? -1 : 1;
}
usort($a,"cmp");
return $a;
}
There HAS to be a much better way to do this. I've been examining ksort(), multisort(), and all sorts of sorts until I'm sort of tired trying to sort it all out.
The situation is this: I've got a 2-d array...
array(
array(3,5,7),
array(2,6,8),
array(1,4,9)
);
...and I want to sort by a column index. Say, column [1], would give this result:
array(
array(1,4,9),
array(3,5,7),
array(2,6,8)
);
Does someone have a link (I'm sure this has been asked before), or could someone say "you need foosort, definitely". Thanks very much.
In the documentation of array_multisort it is mentioned that it can be used for this kind of thing.
You can't avoid creating an array that consists of only one column:
$sort_column = array();
foreach ($a as $row)
$sort_column []= $row[1]; // 1 = your example
array_multisort($sort_column, $a);
This sorts both arrays synchronously so that afterwards your whole array is sorted in the same order as the $sort_column array is.
As of PHP 5.3 you can use a closure (pass $i into the function) by defining your cmp function like this:
$cmp = function($x, $y) use ($i) { ... };
You can use use to access $i:
function cmp($x, $y) use ($i) {
// $i now available
if ($x[$i] == $y[$i]) { return 0; }
return ($x[$i] < $y[$i]) ? -1 : 1;
}
http://www.php.net/manual/en/function.sort.php#99419
phpdotnet at m4tt dot co dot uk
Simple function to sort an array by a specific key. Maintains index association.

Categories