Sort array by two object properties using anonymous function - php

I have the following array:
Array
(
[0] => stdClass Object
(
[timestamp] => 1
[id] => 10
)
[1] => stdClass Object
(
[timestamp] => 123
[id] => 1
)
[2] => stdClass Object
(
[timestamp] => 123
[id] => 2
)
)
I'm currently using the following code to sort the array by the timestamp property:
function sort_comments_by_timestamp(&$comments, $prop)
{
usort($comments, function($a, $b) use ($prop) {
return $a->$prop < $b->$prop ? 1 : -1;
});
}
How can I also sort id by id descending when timestamp is the same?

Suggestion is to send in an array with $props
function sort_comments_by_timestamp(&$comments, $props)
{
usort($comments, function($a, $b) use ($props) {
if($a->$props[0] == $b->$props[0])
return $a->$props[1] < $b->$props[1] ? 1 : -1;
return $a->$props[0] < $b->$props[0] ? 1 : -1;
});
}
And then call it with
sort_comments_by_timestamp($unsorted_array,array("timestamp","id"));
If you want it to work with X number of $props you can make a loop inside the usort always comparing a property with its preceding property in the array like this:
function sort_comments_by_timestamp(&$comments, $props)
{
usort($comments, function($a, $b) use ($props) {
for($i = 1; $i < count($props); $i++) {
if($a->$props[$i-1] == $b->$props[$i-1])
return $a->$props[$i] < $b->$props[$i] ? 1 : -1;
}
return $a->$props[0] < $b->$props[0] ? 1 : -1;
});
}
Cheers!

function sort_comments_by_timestamp(&$comments, $prop)
{
usort($comments, function($a, $b) use ($prop) {
if ($a->$prop == $b->$prop)
return $b->id - $a->id;
else
return $a->$prop < $b->$prop ? 1 : -1;
});
}
The above sorts first by the $prop parameter and then secondary by id.

You can do it with ouzo goodies (I know you've heard about it :P).
$result = Arrays::sort($array,
Comparator::compound(
Comparator::compareBy('timestamp'),
Comparator::compareBy('id')
)
);

I know this is fairly old question however didn't find much info for when you want to sort by multiple properties kinda like mySQL ORDERBY does so here's a function from a personal framework
Option 1: order($key, $direction='asc') where $key is a property of the objects and $direction is 'asc' or 'desc'
Option 2: order(array($key => $direction, $key => $direction))
This is similar to option 1 however when 2 objects have the same value for $key, the second sort option from the array passed is used.
public function order($arr, $key=null, $direction='ASC'){
if(!is_string($key) && !is_array($key))
throw new InvalidArgumentException("order() expects the first parameter to be a valid key or array");
$props = array();
if(is_string($key)) {
$props[$key] = strtolower($direction) == 'asc' ? 1 : -1;
}else{
$i = count($key);
foreach($key as $k => $dir){
$props[$k] = strtolower($dir) == 'asc' ? $i : -($i);
$i--;
}
}
usort($arr, function($a, $b) use ($props){
foreach( $props as $key => $val ){
if( $a->$key == $b->$key ) continue;
return $a->$key > $b->$key ? $val : -($val);
}
return 0;
});
return $arr;
}

$result = -1;
if ($a->timestamp < $b->timestamp) {
$result = 1;
} else if ($a->timestamp === $b->timestamp) {
if ($a->id < $b->id) $result = 1;
}
return $result;
Put this within the usort closure. You can also get rid of the $prop argument and the use ($prop) part.

function compare_city($a, $b)
{
// sort by state
$retval = strnatcmp($a->state, $b->state);
// if identical, sort by city
if(!$retval) $retval = strnatcmp($a->city, $b->city);
return $retval;
}
// sort alphabetically by state and city
usort($sortable, __NAMESPACE__ . '\compare_city');
This function worked with me! Answer found at: Sorting SimpleXMLElement Object arrays

The accepted answer has a loop variant that will not always work.
if($a->$props[$i-1] == $b->$props[$i-1])
return $a->$props[$i] < $b->$props[$i] ? 1 : -1;
obj1 prop1:foo, prop2: bar, prop3: test
obj2 prop1:foo, prop2: bar, prop3: apple
At $i = 1, the first comparison is true, and as bar is not less than bar, it will return -1 rather than continue with the loop to evaluate prop3.
Just an FYI.
I have a variant that also takes direction, so each property can also be sorted by ascending or descending order using -1 and 1, respectively.
function sortByProps(&$anArray, $props) {
usort($anArray, function($a, $b) use ($props) {
for($i = 0; $i < count($props); $i++) {
if( $a->{$props[$i][0]} < $b->{$props[$i][0]}) {
return $props[$i][1];
}
else if( $a->{$props[$i][0]} > $b->{$props[$i][0]}) {
return -1* $props[$i][1];
}
}
return 0;
});
}
sortByProps($someArray,(array(['name',-1],['address',-1],['occupation',-1],['favorite_food',-1])));

Here's how to sort by an arbitrary number of criteria, one after the other, breaking ties. It works a lot like an sql order by col1, col2 clause.
I always forget if $a - $b vs $b - $a sorts ascending or descending. Adjust as needed.
$comparatorSequence = array(
function($a, $b) { return $a->timestamp - $b->timestamp; }
, function($a, $b) { return $a->id - $b->id; }
);
usort($theArray, function($a, $b) use ($comparatorSequence) {
foreach ($comparatorSequence as $cmpFn) {
$diff = call_user_func($cmpFn, $a, $b);
if ($diff !== 0) {
return $diff;
}
}
return 0;
});

Related

Compare the keys and values of three arrays with "array_diff_ukey"

Can somebody explain, what is 1 and -1 in this code: ($a>$b)?1:-1; ?
I know the Array ( [c] => blue ) is returning because the key c is not exist in $a2 and key_compare_func need to return number smaller, equal or bigger then 0.
But I don't understand, how I get the Array ( [c] => blue ), when the key_compare_func returns 0, 1 and -1:
function myfunction($a,$b) {
if ($a === $b) {
return 0;
}
return ($a > $b) ? 1 : -1;
}
$a1=array("a"=>"red","b"=>"green","c"=>"blue");
$a2=array("a"=>"blue","b"=>"black","e"=>"blue");
$result=array_diff_ukey($a1,$a2,"myfunction");
As you can see in array-diff-ukey documentation the "key_compare_func" need to return number smaller, equal or bigger then 0. the numbers 1 and -1 are just example for this results.
In your case you can simply use strcmp as it return the same logic.
You have Array ( [c] => blue ) in the return because the key c is not exist in the $a2 array as it say:
Compares the keys from array1 against the keys from array2 and returns the difference. This function is like array_diff() except the comparison is done on the keys instead of the values.
Edited
Specifically in array-diff-ukey you only need the return 0 because that the way this function is decided the keys are the same so in your example you can define it as:
function myfunction($a,$b) {
if ($a === $b)
return 0;
return 1; // or -1 or 3 or -3 **just not 0**
}
Consider that as the logic behind array-diff-ukey:
array function array-diff-ukey($a1, $a2, $compareFunction) {
$arr = array(); // init empty array
foreach($a1 as $key1 => $value1) { // for each key in a1
$found = false;
foreach($a1 as $key2 => $value2) { //search for all keys in a2
if ($compareFunction($key1, $key2) == 0)
$found = true; // found a key with the same
}
if ($found === false) // add the element only if non is found
$arr[$key1] = $value1;
}
return $arr;
}
If ($a>$b) is true (right after the ?) - you return 1. else (right after the :) will return -1.
It's a short way of writing this:
if ($a>$b) {
return 1;
} else {
return -1;
}
It is the ternary operator in PHP. You can say it as shorthand If/Else. Here is an example:
/* most basic usage */
$var = 5;
$var_is_greater_than_two = ($var > 2 ? true : false); // if $var greater than 2
// return true
// else false
If it is being difficult for you to understand, you can change it with:
if ($a===$b)
{
return 0;
}
else if($a > $b)
{
return 1;
}
else
{
return -1;
}

need help in get values from array

Hi need help getting values from array
first i have get consecutive values from below array eg=>id:1,2 ignore if consecutive values doesn`t not exits
after getting values find the differences between the bids of consecutive values , then sum up all the values of bids(consecutive values) ??
$bidperpage = array([0]=>array(['id']=>1,['bid']='10',['page']='5'),
[1]=>array(['id']=>2,['bid']='15',['page']='5'),
[2]=>array(['id']=>9,['bid']='20',['page']='2'),
[3]=>array(['id']=>3,['bid']='30',['page']='7'),
[4]=>array(['id']=>4,['bid']='40',['page']='7'),
[5]=>array(['id']=>5,['bid']='50',['page']='9'),
[6]=>array(['id']=>6,['bid']='60',['page']='4'),
[7]=>array(['id']=>8,['bid']='70',['page']='4'));
function checkconsecutivevalue($array) {
$ret = array();
$temp = array();
foreach($array as $val) {
if(next($array) == ($val + 1))
$temp[] = $val;
else
if(count($temp) > 0) {
$temp[] = $val;
$ret[] = $temp[0].':'.end($temp);
$temp = array();
}
else
$ret[] = $val;
}
return $ret;
}
If nothing else, your array definition is a flat-out syntax error. It should be
$bidperpage = array(
0 => array('id' => 1, 'bid' => '10', 'page' => '5'),
etc...
);
Note the proper use of the => operator, and the LACK of [] braces.
The easiest way to do this, to my mind is using usort:
$bidperpage = array(array('id'=>1,'bid'=>'10','page'=>'5'),
array('id'=>2,'bid'=>'15','page'=>'5'),
array('id'=>9,'bid'=>'20','page'=>'2'),
array('id'=>3,'bid'=>'30','page'=>'7'),
array('id'=>4,'bid'=>'40','page'=>'7'),
array('id'=>5,'bid'=>'50','page'=>'9'),
array('id'=>11,'bid'=>'60','page'=>'q'),
array('id'=>8,'bid'=>'70','page'=>'4'));
//Sort function
function sortById($a,$b)
{
if ($a['id'] === $b['id'])
{
return 0;
}
return ($a['id'] > $b['id'] ? 1 : -1);
}
usort($bidperpage,'sortById');
More info: See the docsDo remember, I assumed the key id will always be pressent, if this is not the case, you might want to add an extra check to the callback function:
function sortById($a,$b)
{
if (!isset($a['id']) && !isset($b['id']))
{
return 0;
}
if (!isset($a['id']))
{
return 1;//move to end of array
}
if (!isset($b['id']))
{
return -1;
}
if ($a['id'] === $b['id'])
{
return 0;
}
return ($a['id'] > $b['id'] ? 1 : -1);
}
This function needn't be that verbose, but since I get the impression you haven't tried that much, I'll leave that up to you.

Shuffle by highest number contained with in an array

This is trapped inside a PHP foreach where there are multiple results being fetched.
$frontpage[] = array(
'perc' => $percentage,
'id' => $result->ID
);
I then want to sort $frontpage in descending order according to the values contained in 'perc', all of which are numbers. How do I do that?
Have you tried to use uasort()? It's a function with which you define a callback function that compares certain values.
function customCompare($a, $b)
{
if ($a['perc'] == $b['perc']) {
return 0;
}
return ($a['perc'] < $b['perc']) ? -1 : 1;
}
uasort($frontpage, 'customCompare');
$frontpage = array_reverse($frontpage); // for descending order
See it in action here.
There are loads of examples on how to use usort here: http://php.net/manual/en/function.usort.php
I wrote a simple test example assuming that the 'perc' key in the array is always the first one.
<?php
function percentCompare($a, $b)
{
if ($a == $b)
return 0;
//we want it decending
return ($a > $b) ? -1 : +1;
}
$frontpage[] = array();
//Fill the array with some random values for test
for ($i = 0; $i < 100; $i++)
{
$frontpage[$i] = array(
'perc' => rand($i, 100),
'id' => $i
);
}
//Sort the array
usort($frontpage, 'percentCompare');
print_r($frontpage);
?>

How to sort an array of arrays in php?

In php I have a numerical array of associative arrays:
mainArray:
[
array1:['title':'Record a','order':'2'],
array2:['title':'Record b','order':'4'],
array3:['title':'Record c','order':'1'],
array4:['title':'Record d','order':'3']
]
What is the simplest way to sort mainArray by the 'order' value of each associative array?
Thanks
You can use usort function. Since PHP 5.4 you can use closure function:
usort($mainArray, function ($a, $b) {
$a_val = (int) $a['order'];
$b_val = (int) $b['order'];
if($a_val > $b_val) return 1;
if($a_val < $b_val) return -1;
return 0;
});
Or version for PHP < 5.4:
usort($mainArray, 'myCompare');
function myCompare($a, $b) {
$a_val = (int) $a['order'];
$b_val = (int) $b['order'];
if($a_val > $b_val) return 1;
if($a_val < $b_val) return -1;
return 0;
}
The simplest version, using comparison function and usort:
usort($mainArray, function($a, $b) {
return $a['order'] - $b['order'];
});
I found this in the php documentation comments for asort() See also the sort() page, in the comments there are a few good candidates.
function named_records_sort($named_recs, $order_by, $rev=false, $flags=0)
{// Create 1-dimensional named array with just
// sortfield (in stead of record) values
$named_hash = array();
foreach($named_recs as $key=>$fields)
$named_hash["$key"] = $fields[$order_by];
// Order 1-dimensional array,
// maintaining key-value relations
if($reverse) arsort($named_hash,$flags=0) ;
else asort($named_hash, $flags=0);
// Create copy of named records array
// in order of sortarray
$sorted_records = array();
foreach($named_hash as $key=>$val)
$sorted_records["$key"]= $named_recs[$key];
return $sorted_records;} // named_recs_sort()
function show_sorted_records($named_recs, $order_by, $rev=false, $flags=0)
{$sorted_records=named_records_sort($named_recs, $order_by, $rev, $flags);
foreach($sorted_records as $name=>$fields)
{echo "<b>$name</b> ";
foreach($fields as $field=>$val)
echo "$field = $val "; echo "<br>";}
} // show_sorted_records()
$girl_friends=array();
$girl_friends["Anna"]=
array("born"=>'1989-08-22',"cupsize"=>'B-',"IQ"=>105, "daddy"=>'rich');
$girl_friends["Zoe"]
=array("born"=>'1978-03-11',"cupsize"=>'C#',"IQ"=>130, "daddy"=>'poor');
$girl_friends["Lilly"]
=array("born"=>'1985-06-16',"cupsize"=>'DD',"IQ"=>90, "daddy"=>'nasty');
$order_by="cupsize"; echo "And the winners are: <br>";
show_sorted_records($girl_friends, $order_by, true);

PHP: Sort Multidimensional Array with Different Depth per element by Field

i have have got an array of the complexe sort to store my navigation (which is supposed to be changed by the user afterwards). I do not want to have the script only work with 3 levels of depth so I am looking for a nice and good way to sort this array by the position field.
$nav[1]=array(
'name'=>'home',
'position'=>'2',
children=>array(
[1]=array(
'name'=>'page2',
position=>'3'),
[2]=array(
'name'=>'page3',
'position'=>'1'),
[3]=array(
'name'=>'page4',
'position'=>'2')
)
$nav[2]=array(
'name'=>'Second level 1',
'position'=>'1'
);
I hope someone can help me, thanks for thinking about the problem.
Sort each children array recursively. For example:
function cmp($a, $b)
{
$ap = intval($a['position']);
$bp = intval($b['position']);
if ($ap == $bp) {
return 0;
}
return ($ap < $bp) ? -1 : 1;
}
function sort_menu(&$item)
{
if ($item['children']) {
foreach ($item['children'] as &$child) {
sort_menu($child);
}
usort($item['children'], "cmp");
}
}
$tmp = array('children' => $nav);
sort_menu($tmp);
$nav = $tmp['children'];
Here is an example of usort.
function yourSortFunction($a, $b)
{
if ($a['position'] == $b['position']) {
return 0;
}
return ($a['position'] < $b['position']) ? -1 : 1;
}
usort($nav, "yourSortFunction");'
You can call it in your $nav array in recursion in other function.

Categories