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);
Related
I am working on usort function, right now I have the array like this
$data = array(
array('rank'=>8,'suit'=>0),
array('rank'=>7,'suit'=>3),
array('rank'=>8,'suit'=>2),
);
When I am using usort function, it is as below
usort($data function($a, $b) {
return $a['rank'] - $b['rank'];
});
And i am getting below result :
$data = array(
array('rank'=>7,'suit'=>3),
array('rank'=>8,'suit'=>0),
array('rank'=>8,'suit'=>2),
);
But i want below result :
$data = array(
array('rank'=>7,'suit'=>3),
array('rank'=>8,'suit'=>2),
array('rank'=>8,'suit'=>0),
);
I want to first do ascending by rank, and if rank has the same value then I want to do descending order by suit, Can anyone please help me how can I achieve this result with usort function?
Still using usort(), this will sort rank in ascending order suit in descending order:
usort($data, function($a, $b) {
if ($a['rank'] == $b['rank']) {
if ($a['suit'] == $b['suit']) {
return 0;
}
return $a['suit'] > $b['suit'] ? -1 : 1;
}
return $a['rank'] < $b['rank'] ? -1 : 1;
});
I came across this logic by doing this several time using PHP 7's spaceship operator.
Here's the same solution using this operator to sort the rank key:
usort($data, function($a, $b) {
if ($a['rank'] == $b['rank']) {
if ($a['suit'] == $b['suit']) {
return 0;
}
return $a['suit'] > $b['suit'] ? -1 : 1;
}
return $a['rank'] <=> $b['rank'];
});
You can try to use array_multisort
foreach ($data as $key => $value) {
$rank[$key] = $value['rank'];
$suit[$key] = $value['suit'];
}
array_multisort($rank, SORT_ASC, $suit, SORT_DESC, $data);
Or as this answer you can use
array_multisort(array_column($data, 'rank'), SORT_ASC, array_column($data, 'suit'), SORT_DESC, $data);
I have formed this array of objects using this:
foreach($snapdealProductCollection as $snapdealProduct){
$item = new stdClass();
$item->id = $snapdealProduct->getId();
$item->vendortype=2;
$item->name = $snapdealProduct->getTitle();
$item->imgsrc = $snapdealProduct->getImageLink();
$item->price = $snapdealProduct->getEffectivePrice();
$item->source = "Snapdeal.com";
$item->redirectUrl = $snapdealProduct->getLink().$affProgram;
$item->type = $snapedealType[$snapdealProduct->getId()];
$item->simid = $snapdealsimid[$snapdealProduct->getId()];
$item->stype = 2;
$i++;
array_push($items, $item);
}
I need to sort firstly by type, then by simid. How should I sort it?
Full code:
$unsortedItems = $this->getSimilarItems($pid);
$vendors=$this->getAllVendors();
usort($unsortedItems , array($this, "cmp"));
function cmp($a, $b)
{
return strcmp($a->type, $b->type) || strcmp($a->simid, $b->simid);
}
$unSortedItems is the array returned from the foreach block
You can use usort() function for this, and your comparison function should be like this:
function cmp($a, $b){
if(strcmp($a->type, $b->type) == 0){
if ($a->simid == $b->simid) {
return 0;
}
return ($a->simid < $b->simid) ? -1 : 1;
}
return strcmp($a->type, $b->type);
}
usort($unsortedItems , array($this, "cmp"));
Here are the relevant references:
usort()
strcmp()
Basically you compare the first field first and the second field second :-)
function cmp($a, $b) {
return strcmp($a->type, $b->type) || strcmp($a->simid, $b->simid);
}
If the first compare returns 0, the second one will be evaluated.
You can write the same longer: if first fields are equal, compare another fields, else return the result of the first comparison.
I have created method to sort array with values like this: array('regdate','birthday','editdate') which should sort the elements in the way that the elements containing word date should be moved to left like this array('regdate','editdate','birthday')
public function sortColumnsBySubstring($haystack, $substr){
if ($haystack == $substr) {
return 0;
}
return strpos($substr) !== false ? -1 : 1;
}
However it is not clear to me how to make this working. Example which I have found in php manual shows function with no arguments or closures - I use php version 5.2 so I cannot use closures.
All I can think up is this usort($date_cols, $this->sortColumnsBySubstring($value, 'date') but here $value is undefined so it's not solution.
Question is how to implement the function to work correctly?
You need to pass the callback as an array:
usort($date_cols, [$this, 'sortColumnsBySubstring']);
See Callbacks / Callables in PHP docs.
First solution is to my original question:
function cmp($a, $b)
{
$adate = (strpos($a, 'date') !== false);
$bdate = (strpos($b, 'date') !== false);
if (!($adate ^ $bdate)) return strcmp($a, $b);
return $adate ? -1 : 1;
}
$a = array('birthday', 'regdate', 'editdate');
usort($a, 'cmp');
Second solution uses splitting into two arrays, sort and then merge them back. I have tried to use more word related to time to identify the values related to time.
private function getDateColumns(&$array)
{
$search_date_columns = array('date','datetime','timestamp','time','edited','changed','modified','created','datum');
$result = array( array(), array() );
foreach($array as $v1):
$found = false;
foreach($search_date_columns as $v2)
if ( strpos($v1, $v2)!==false )
{ $found = true; break; }
if ($found)
$result[0][] = $v1;
else
$result[1][] = $v1;
endforeach;
return $result;
}
Which is implemented like that:
$date_cols = array('regdate','time','editdate','createdate','personal','mojedatum','edited','test','modified','changed','pokus','timestamp','hlava');
$arrays = $this->getDateColumns($date_cols);
rsort($arrays[0]);
$date_cols = array_merge($arrays[0], $arrays[1]);
unset($arrays);
print_r($date_cols);
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;
});
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);
?>