Hey guys I'm trying to reverse sort a multidimensional array with usort but am messing up somewhere. Here's my code:
$array = array(
array(123 => 'foo'), // duplicate
array(124 => 'foo'),
array(127 => 'foo'),
array(126 => 'foo'),
array(123 => 'foo'), // duplicate
array(125 => 'foo'),
);
function rcmp($a, $b) {
if($a == $b) {
return 0;
}
return ($a < $b) ? -1 : 1;
}
usort($array, 'rcmp');
print_r($array);
/*
Array
(
[0] => Array ( [126] => foo) )
[1] => Array ( [125] => foo) )
[2] => Array ( [127] => foo) )
[3] => Array ( [123] => foo) )
[4] => Array ( [124] => foo) )
[5] => Array ( [123] => foo) )
)
*/
I am expecting
/*
Array
(
[0] => Array ( [127] => foo) )
[1] => Array ( [126] => foo) )
[2] => Array ( [125] => foo) )
[3] => Array ( [124] => foo) )
[4] => Array ( [123] => foo) )
[5] => Array ( [123] => foo) )
)
*/
What am I doing wrong?
If you want to compare on the array's indexes, you must extract the indexes in the comparison function and reverse the comparison for descending order
function rcmp($a, $b) {
$a = array_keys($a);
$b = array_keys($b);
if($a[0] == $b[0]) {
return 0;
}
return ($a[0] < $b[0]) ? 1 : -1;
}
When your comparison function rcmp is invoked, its arguments $a and $b are arrays so your equality and less-than operators have arrays as their operands.
The equality operator on arrays works like this:
TRUE if $a and $b have the same key/value pairs.
The less-than comparison works like this:
Array with fewer members is smaller, if key from operand 1 is not found in operand 2 then
arrays are uncomparable, otherwise - compare value by value.
"Uncomparable" means the comparison evaluates to null, which in turn causes rcmp to return 1.
So what ends up happening is that when $a and $b are not identical arrays rcmp always returns 1 (i.e. considers that $a is greater). This happens irrespective of what the key in each array is, and as a result you get a meaningless ordering.
If you wanted to order these arrays by their first key with usort, you would do it like this:
function rcmp($a, $b)
{
return key($a) - key($b);
}
Here's a solution using create_function that I came up with, accepting the best answer though. Thanks and hope this helps someone else out.
$array = array(
array(124 => 'foo'),
array(123 => 'foo'), // duplicate
array(127 => 'foo'),
array(126 => 'foo'),
array(123 => 'foo'), // duplicate
array(125 => 'foo'),
);
usort($array, create_function('$a, $b','return (key($a) == key($b))
? 0
: (key($a) < key($b))
? -1
: 1;'));
print_r($array);
Related
Today I provided the answer of this question and I wrote a script, but I found out that something went wrong.
Here is the first script
<?php
$array = array(
"0" => array (
"id" => 1204,
"custom_price" => 33.1500
),
"1" => array (
"id" => 1199,
"custom_price" => 15.83
),
"2" => array (
"id" => 1176,
"custom_price" => 16.83
)
);
usort($array, function($a, $b) {
return $a['custom_price'] - $b['custom_price'];
});
echo "<pre>";
print_r($array);
and its output is (also you can check output on sandbox)
<pre>Array
(
[0] => Array
(
[id] => 1176
[custom_price] => 16.83
)
[1] => Array
(
[id] => 1199
[custom_price] => 15.83
)
[2] => Array
(
[id] => 1204
[custom_price] => 33.15
)
)
So, my desired output should be sort like (custom_price 15.83, 16.83, 33.15000) but the actual output is (custom_price 16.83,15.83,33.15000). you can see 15.83 is smallest from 16.83. the sorting result is wrong
So, when I change custom_price 15.83 to 14.83 then sorting output is correct
<pre>Array
(
[0] => Array
(
[id] => 1199
[custom_price] => 14.83
)
[1] => Array
(
[id] => 1176
[custom_price] => 16.83
)
[2] => Array
(
[id] => 1204
[custom_price] => 33.15
)
)
you can see output on sandbox
I can't understand what's going on.. any idea about this ?
My Question is: I check each iteration but can't identify the problem. when custom_price is 15.83 then result is wrong. why?
There is a warning in the PHP manual about the return values from the usort() compare function (at http://php.net/manual/en/function.usort.php#refsect1-function.usort-parameters)...
Caution Returning non-integer values from the comparison function,
such as float, will result in an internal cast to integer of the
callback's return value. So values such as 0.99 and 0.1 will both be
cast to an integer value of 0, which will compare such values as
equal.
Also from PHP 7. you can use the spaceship operator <=> which returns 1, 0, -1 depending on the comparison of the two values...
usort($array, function($a, $b) {
return $a['custom_price'] <=> $b['custom_price'];
});
echo "<pre>";
print_r($array);
There is a complete example in the PHP manual about usort. Here's the modified version to solve your problem:
<?php
function cmp($a, $b)
{
if ($a['custom_price'] == $b['custom_price']) {
return 0;
}
return ($a['custom_price'] < $b['custom_price']) ? -1 : 1;
}
Below code will solve your problem,
usort($array, function($a, $b) {
if($a['custom_price']==$b['custom_price']) return 0;
return $a['custom_price'] > $b['custom_price'] ? 1 : -1;
});
Updated function
usort($array, function($a, $b) {
return $a['custom_price'] > $b['custom_price'];
});
I have this array and I want to sort it (ASC)?
$stand_array[$player_name][$player_points] = $player_rank;
print_r
Array
(
[Player1] => Array
(
[50] => 5.7
)
[Player2] => Array
(
[40] => 4.2
)
[Player3] => Array
(
[30] => 3.7
)
[Player4] => Array
(
[20] => 2.3
)
[Player5] => Array
(
[10 => 1.5
)
[Player6] => Array
(
[60] => 6.3
)
)
Would you like to help me to solve this array on $player_rank (ASC)?
NB: I tried this function, but it did not work:
function sortByOrder($a, $b) {
return $a[$player_rank] - $b[$player_rank];
}
usort($myArray, 'sortByOrder');
The variable $player_rank is not visible from the sortByOrder function scope. Also, the indexes is different in each player array, so you need to access it like this:
function sortByOrder($a, $b)
{
$a = end($a);
$b = end($b);
if ($a == $b)
{
return 0;
}
return ($a < $b) ? -1 : 1;
}
usort($myArray, 'sortByOrder');
And if you want to save keys in $myArray then you must use the uasort function instead of usort.
What's the easiest way to sort a multi dimensional array. I need to sort it so the output is displayed with the highest count of values on the sub arrays then the sorted alphabetically by the key of the sub arrays:
So something like this:
MyArray(
subarray4('val1')
subarray2('val1')
subarray3('val1', 'val2', 'val3')
subarray1('val1', 'val2', 'val3')
)
Would look more like this:
MyArray(
subarray1('val1', 'val2', 'val3')
subarray3('val1', 'val2', 'val3')
subarray2('val1')
subarray4('val1')
)
Two steps here, first sort array according to subarray count, then loop through array sorting each subarray "alphanumerically".
$myArray = array(
array('val1', 'val2'),
array('val3'),
array('val7', 'val6', 'val5', 'val4'),
array('val8', 'val9', 'val10'),
);
// sort primary array by subarray count
uasort($myArray, function ($a, $b) {
$a = count($a);
$b = count($b);
return ($a == $b) ? 0 : (($a > $b) ? -1 : 1);
});
// now sort each subarray alphanumerically
foreach ( $myArray as $key => $arr ) {
natsort($myArray[$key]); // letters - alpha, numbers - numeric
// note: ignoring $arr because sorting it does not affect $myArray
}
print '<pre>';
print_r($myArray);
print '</pre>';
Output:
Array
(
[2] => Array
(
[3] => val4
[2] => val5
[1] => val6
[0] => val7
)
[3] => Array
(
[0] => val8
[1] => val9
[2] => val10
)
[0] => Array
(
[0] => val1
[1] => val2
)
[1] => Array
(
[0] => val3
)
)
The uasort sorting algorithm is cut and paste from here: Sorting array by count of subarray
$array = array(
array('val1'),
array('val1'),
array('val1', 'val2', 'val3'),
array('val1', 'val2', 'val3'),
);
function sortByCount($a, $b) {
$a = count($a);
$b = count($b);
if($a == $b) {
return 0;
}
return ($a > $b) ? -1 : 1;
}
uasort($array, 'sortByCount');
How to get the difference of arrays containing data-types?
First array:
Array
(
[0] => Array
(
[ID] => 21323154
[NAME] => Name_2
[PREVIEW_TEXT] => Text_2
)
)
Second array:
Array
(
[0] => Array
(
[ID] => 543547564
[NAME] => Name_1
[PREVIEW_TEXT] => Text_1
)
[1] => Array
(
[ID] => 222213322
[NAME] => Name_2
[PREVIEW_TEXT] => Text_2
)
[2] => Array
(
[ID] => 333876833
[NAME] => Name_3
[PREVIEW_TEXT] => Text_3
)
)
The result should be an array:
Array
(
[0] => Array
(
[ID] => 543547564
[NAME] => Name_1
[PREVIEW_TEXT] => Text_1
)
[1] => Array
(
[ID] => 333876833
[NAME] => Name_3
[PREVIEW_TEXT] => Text_3
)
)
I tried different options, but they all return the result found the difference, i.e. the first array.
I have a different ID
Just try with:
$output = array_udiff($arraySecond, $arrayFirst, function($a, $b){
return strcmp($a['NAME'], $b['NAME']);;
});
Output:
array(2) {
[0] =>
array(3) {
'ID' =>
int(543547564)
'NAME' =>
string(6) "Name_1"
'PREVIEW_TEXT' =>
string(6) "Text_1"
}
[2] =>
array(3) {
'ID' =>
int(333876833)
'NAME' =>
string(6) "Name_3"
'PREVIEW_TEXT' =>
string(6) "Text_3"
}
}
If both arrays are large, array_diff will be very inefficient, because it compares each element of the first array with each element of the second. A faster solution is to split the process into two steps: first, generate a set of keys to remove:
$remove = array();
foreach($firstArray as $item)
$remove[$item['name']] = 1;
and then iterate over the second array and add "good" items to the result:
$result = array();
foreach($secondArray as $item)
if(!isset($remove[$item['name']]))
$result []= $item;
This will give you linear performance, while array_diff is quadratic.
Diff both arrays against the field (column) you want to, give back only those entries then (thanks to keys):
$col = 'NAME';
$diff = array_intersect_key($b, array_diff(array_column($b, $col), array_column($a, $col)));
print_r($diff);
If you prefer to have this less quadratic but linear, you can also solve it via iteration (inspired by georg's answer but using array_column() again):
$filtered = function ($col, $a, $b) {
return iterator_to_array(call_user_func(function () use ($col, $a, $b) {
$coled = array_flip(array_column($a, $col));
foreach ($b as $bk => $bv) if (!isset($coled[$bv[$col]])) yield $bk => $bv;
}));
};
print_r($filtered('NAME', $a, $b));
With $a and $b as outlined, you will get the following result for both examples:
Array
(
[0] => Array
(
[ID] => 543547564
[NAME] => Name_1
[PREVIEW_TEXT] => Text_1
)
[2] => Array
(
[ID] => 333876833
[NAME] => Name_3
[PREVIEW_TEXT] => Text_3
)
)
// 1.) - diff against column, intersect keys
$filtered = function($col, $a, $b) {
return array_intersect_key($b, array_diff(array_column($b, $col), array_column($a, $col)));
};
// 2.) - iterate and take only unset by column
$filtered = function ($col, $a, $b) {
return iterator_to_array(call_user_func(function () use ($col, $a, $b) {
$coled = array_flip(array_column($a, $col));
foreach ($b as $bk => $bv) if (!isset($coled[$bv[$col]])) yield $bk => $bv;
}));
};
// 3.) - array_udiff against column
$filtered = function($col, $a, $b) {
return array_udiff($b, $a, function ($a, $b) use ($col) {
return strcmp($a[$col], $b[$col]);
});
};
Usage:
print_r($filtered('NAME', $a, $b));
Where $a is the array containing the elements to remove from the $b array. So $a is the first array of the question and $b is the second array of the question.
This question already has answers here:
How to Sort a Multi-dimensional Array by Value
(16 answers)
Closed 10 years ago.
I have an array with a bunch of keys. I want to sort one of the keys by their values.
Array (
[0] => stdClass Object (
[id] => 1
[question] => Action
[specific_to_movie_id] => 1
[total_yes] => 4 )
[1] => stdClass Object (
[id] => 2
[question] => Created by DC Comics
[specific_to_movie_id] => 1
[total_yes] => 1 )
[2] => stdClass Object (
[id] => 3
[question] => Christian Bale
[specific_to_movie_id] => 1
[total_yes] => 1 )
)
The array looks like that above, and I want to sort by "Total_yes"
How can I go about doing this in PHP?
Because it's a little more complex than a standard array sort, you'll need to use usort:
function compare_items( $a, $b ) {
return $a->total_yes < $b->total_yes;
}
$arrayToSort = array (
(object) array(
'id' => 1,
'question' => 'Action',
'specific_to_movie_id' => 1,
'total_yes' => 4
),
(object) array(
'id' => 2,
'question' => 'Created by DC Comics',
'specific_to_movie_id' => 1,
'total_yes' => 1
),
(object) array(
'id' => 3,
'question' => 'Christian Bale',
'specific_to_movie_id' => 1,
'total_yes' => 1
)
);
usort($arrayToSort, "compare_items");
If you want to reverse the sort order, just change return $a->total_yes < $b->total_yes to use > (greater than) instead of < (less than)
you could use usort, like:
function cmp($a, $b) {
return $a < $b;
}
usort($your_array, "cmp");
You can use Usort() that use a specific compere function:
Definition and Usage
The usort() function sorts an array using a user-defined comparison
function.
Syntax
usort(array,myfunction);
array -Required. Specifies the array to sort
myfunction-Optional. A string that define a callable comparison function. The comparison function must return an integer <, =, or > than 0 if the first argument is <, =, or > than the second argument
<?php
function cmp($a, $b)
{
if ($a->total_yes == $b->total_yes) {
return 0;
}
return ($a->total_yes < $b->total_yes) ? -1 : 1;
}
usort($array, "cmp");
?>
You have object, therefore you need use [usort()][http://www.php.net/manual/en/function.usort.php]
usort($array, function($a, $b){
if ($a->total_yes == $b->total_yes)
return 0;
return ($a->total_yes > $b->total_yes) ? -1 : 1;});
print_r($array);