sorting an array between array and string values - php

I am creating a template parser and i need to sort the array of variables that gets passed into the parser so that each array element that IS an array get processed first. For example:
$data = array(
'name' => 'Steven',
'type' => array(
'gender' => 'M',
'age' => 23'
)
)
I need 'type' to be first since it is an array, and 'name' to be last. Does anyone have an idea on how to do this? Ive looked at php.net's manual at the different sort functions, but dont see how any apply to my situation.

function cmp($a, $b) {
return is_array($a) ? -1 : 1;
}
uasort($data, "cmp");
This will sort the array based on the value type...alphabetically. Array comes before String.

function cmp($a, $b) {
return is_array($a)? -1 : 0;
}
uasort($data, "cmp");

Related

Custom sort an associative array by its keys using a lookup array

I need to sort the following flat, associative array by its keys, but not naturally. I need to sort the keys by a predefined array of values.
$aShips = [
'0_204' => 1,
'0_205' => 2,
'0_206' => 3,
'0_207' => 4
];
My order array looks like this:
$order = ["0_206", "0_205", "0_204", "0_207"];
The desired result:
[
'0_206' => 3,
'0_205' => 2,
'0_204' => 1,
'0_207' => 4
]
I know how to write a custom sorting function, but I don't know how to integrate the order array.
function cmp($a, $b){
if ($a==$b) return 0;
return ($a<$b)?-1:1;
}
uasort($aShips, "cmp");
If you want to sort by keys. Use uksort. Try the code below.
<?php
$aShips = array('0_204' => 1, '0_205' => 2, '0_206' => 3, '0_207' => 4);
uksort($aShips, function($a, $b) {
return $b > $a;
});
According to the Official PHP Docs, you can use uasort() like this (just for demo):
<?php
// Comparison function
function cmp($a, $b) {
if ($a == $b) {
return 0;
}
return ($a < $b) ? -1 : 1;
}
// Array to be sorted
$array = array('a' => 4, 'b' => 8, 'c' => -1, 'd' => -9, 'e' => 2, 'f' => 5, 'g' => 3, 'h' => -4);
print_r($array);
// Sort and print the resulting array
uasort($array, 'cmp');
print_r($array);
?>
You don't need to leverage a sorting algorithm.
Simply flip $order to use its values as keys (maintaining their order), then merge the two arrays to replace the unwanted values of $order with the values from $aShip.
This assumes that $aShip includes all key values represented in $order, of course. If not, array_intersect_key() can be used to filter $order, but this is dwelling on a fringe case not included in the posted question.
Code: (Demo)
var_export(array_replace(array_flip($order), $aShips));
Output:
array (
'0_206' => 3,
'0_205' => 2,
'0_204' => 1,
'0_207' => 4,
)
This also works when you haven't listed every occurring key in $order -- the unlisted keys are "moved to the back". Proven by this Demo.
Using a custom sorting algorithm can be done and there will be a number of ways, but I'll only show one for comparison's sake.
Reverse your order array and use it as a lookup array while asking uksort() to sort in a descending fashion. If an encountered key is not found in the lookup assign it a value of -1 to ensure it moves to the back of the array.
Code: (Demo)
$lookup = array_flip(array_reverse($order));
uksort($aShips, function($a, $b) use ($lookup) {
return ($lookup[$b] ?? -1) <=> ($lookup[$a] ?? -1);
});
var_export($aShips);
If you don't like reversing the lookup and sorting DESC, you can count the order array to determine a fallback value. This alternative script uses arrow function syntax for brevity and to gain direct access to the variables declared outside of the closure. (Demo)
$lookup = array_flip($order);
$default = count($order);
uksort($aShips, fn($a, $b) => ($lookup[$a] ?? $default) <=> ($lookup[$b] ?? $default));
var_export($aShips);

Sorting Array by Key (Key is a string)

I have a simple Array. The goal is, to sort them ascending by the key.
$someUnsortedArray = array("140/142" => "FirstValue", "118/120" => "SecondValue", "122/124" => "ThirdValue", "40/42" => "FourthValue");
ksort($someUnsortedArray);
My Output:
array (size=4)
'118/120' => string 'SecondValue'
'122/124' => string 'ThirdValue'
'140/142' => string 'FirstValue'
'40/42' => string 'FourthValue'
Expected Output:
array (size=4)
'40/42' => string 'FourthValue'
'118/120' => string 'SecondValue'
'122/124' => string 'ThirdValue'
'140/142' => string 'FirstValue'
What's the function in php I am searching for?
You could use uksort() in this case:
$someUnsortedArray = array("140/142" => "FirstValue", "118/120" => "SecondValue", "122/124" => "ThirdValue", "40/42" => "FourthValue");
uksort($someUnsortedArray, function($a, $b){
$a = str_replace('/', '', $a);
$b = str_replace('/', '', $b);
return $a - $b;
});
echo '<pre>';
print_r($someUnsortedArray);
As an alternative, you can also make use of the natural order string compare function to compare the keys
function sortKey($a, $b) {
return strnatcmp($a, $b);
}
uksort($someUnsortedArray,"sortKey");
Checking php manual:
http://php.net/manual/en/function.ksort.php
Use ksort() with SORT_NUMERIC flag.
$someUnsortedArray = array("140/142" => "FirstValue", "118/120" => "SecondValue", "122/124" => "ThirdValue", "40/42" => "FourthValue");
ksort($someUnsortedArray, SORT_NUMERIC);
echo '<pre>';
print_r($someUnsortedArray);
Sample Output
Use the function uksort and pass the function a custom function/method that will do the right conversion based on your needs.
Here is the PHP manual page for he function uksort
http://php.net/manual/en/function.uksort.php
The result is the expected behaviour, because if you sort Strings in alphanumerical order 1* is always before 4*.
If you want to have it sorted by number you'll have to split your string, convert the elements to number and sort them by number.
Implementation depends on what you want to achieve. Just a list of the Keys? Then you could iterate in an foreach loop adressing the keys for example.
There won't be an "out-of-the-box" PHP Function.
$someUnsortedArray = array("5/142" => "FirstValue", "118/120" => "SecondValue", "122/124" => "ThirdValue", "40/42" => "FourthValue");
uksort($someUnsortedArray, function($a, $b) {
// to avoid manipulating them as a string ....
return ((float)$a)-((float)$b);
});
var_dump($someUnsortedArray);

Sort an object that contains an array by a specific key, DESC

I'm using the https://github.com/brunogaspar/cartify library to manage my Cart. Things are good, except the fact that I need to be able to sort the items contained within the array by the price key DESC
If I'm using the Cartify::cart()->contents(), here's an example of one of the items contained:
array(
'id' => 'sku_123ABC',
'qty' => 1,
'price' => 39.95,
'name' => 'T-Shirt',
'options' => array(
'size' => 'L',
'color' => 'Red'
)
As you can see, there's a price key ... and if I can't sort on the class level, I'll have to sort these items after the fact.
You can do this using usort and a custom compare function. Something like this:
usort($myArray, function ($a, $b) {
if ($a['price'] == $b['price']) {
return 0;
}
return ($a['price'] < $b['price']) ? 1 : -1;
});
Not the most elegant solution to hard-code the key into the sort function, but it will work.

find max value in associative array with condition

$a=2;
$b=6;
$c=7;
$r1=8;
$r2=9;
$r3=6;
$array = array(
array('MIDDAY'=>$a,'RATE'=>$r1),
array('SHORTDAY'=>$b,'RATE'=>$r2),
array('LONGDAY'=>$c,'RATE'=>$r3)
);
I have a array like this and i want this array to process
and want to check whose count is greater like as above LONGDAY=7
then i want this LONGDAY key in one variable and its RATE in other variable
And also want to check if two count is equal then like LONGDAY=7 and MIDDAT=7
then i want to check with RATE whose rate is greater then same
i want this LONGDAY key in one variable and its RATE in other variable (for RATE is greater in this case)
Something along these lines should do it to sort the array according to your rules and take the top result.
$array = array(
array('type' => 'MIDDAY', 'val' => $a, 'rate' => $r1),
array('type' => 'SHORTDAY', 'val' => $b, 'rate' => $r2),
array('type' => 'LONGDAY', 'val' => $c, 'rate' => $r3)
);
usort($array, function ($a, $b) {
if ($a['val'] < $b['val']) return 1;
else if ($a['val'] > $b['val']) return -1;
else return $b['rate'] - $a['rate'];
});
$var1 = $array[0]['type'];
$var2 = $array[0]['rate'];
Note, this uses PHP 5.3+ anonymous function syntax.

How do I Sort a Multidimensional Array in PHP [duplicate]

This question already has answers here:
How can I sort arrays and data in PHP?
(14 answers)
Closed 8 years ago.
I have CSV data loaded into a multidimensional array. In this way each "row" is a record and each "column" contains the same type of data. I am using the function below to load my CSV file.
function f_parse_csv($file, $longest, $delimiter)
{
$mdarray = array();
$file = fopen($file, "r");
while ($line = fgetcsv($file, $longest, $delimiter))
{
array_push($mdarray, $line);
}
fclose($file);
return $mdarray;
}
I need to be able to specify a column to sort so that it rearranges the rows. One of the columns contains date information in the format of Y-m-d H:i:s and I would like to be able to sort with the most recent date being the first row.
Introducing: a very generalized solution for PHP 5.3+
I 'd like to add my own solution here, since it offers features that other answers do not.
Specifically, advantages of this solution include:
It's reusable: you specify the sort column as a variable instead of hardcoding it.
It's flexible: you can specify multiple sort columns (as many as you want) -- additional columns are used as tiebreakers between items that initially compare equal.
It's reversible: you can specify that the sort should be reversed -- individually for each column.
It's extensible: if the data set contains columns that cannot be compared in a "dumb" manner (e.g. date strings) you can also specify how to convert these items to a value that can be directly compared (e.g. a DateTime instance).
It's associative if you want: this code takes care of sorting items, but you select the actual sort function (usort or uasort).
Finally, it does not use array_multisort: while array_multisort is convenient, it depends on creating a projection of all your input data before sorting. This consumes time and memory and may be simply prohibitive if your data set is large.
The code
function make_comparer() {
// Normalize criteria up front so that the comparer finds everything tidy
$criteria = func_get_args();
foreach ($criteria as $index => $criterion) {
$criteria[$index] = is_array($criterion)
? array_pad($criterion, 3, null)
: array($criterion, SORT_ASC, null);
}
return function($first, $second) use (&$criteria) {
foreach ($criteria as $criterion) {
// How will we compare this round?
list($column, $sortOrder, $projection) = $criterion;
$sortOrder = $sortOrder === SORT_DESC ? -1 : 1;
// If a projection was defined project the values now
if ($projection) {
$lhs = call_user_func($projection, $first[$column]);
$rhs = call_user_func($projection, $second[$column]);
}
else {
$lhs = $first[$column];
$rhs = $second[$column];
}
// Do the actual comparison; do not return if equal
if ($lhs < $rhs) {
return -1 * $sortOrder;
}
else if ($lhs > $rhs) {
return 1 * $sortOrder;
}
}
return 0; // tiebreakers exhausted, so $first == $second
};
}
How to use
Throughout this section I will provide links that sort this sample data set:
$data = array(
array('zz', 'name' => 'Jack', 'number' => 22, 'birthday' => '12/03/1980'),
array('xx', 'name' => 'Adam', 'number' => 16, 'birthday' => '01/12/1979'),
array('aa', 'name' => 'Paul', 'number' => 16, 'birthday' => '03/11/1987'),
array('cc', 'name' => 'Helen', 'number' => 44, 'birthday' => '24/06/1967'),
);
The basics
The function make_comparer accepts a variable number of arguments that define the desired sort and returns a function that you are supposed to use as the argument to usort or uasort.
The simplest use case is to pass in the key that you 'd like to use to compare data items. For example, to sort $data by the name item you would do
usort($data, make_comparer('name'));
See it in action.
The key can also be a number if the items are numerically indexed arrays. For the example in the question, this would be
usort($data, make_comparer(0)); // 0 = first numerically indexed column
See it in action.
Multiple sort columns
You can specify multiple sort columns by passing additional parameters to make_comparer. For example, to sort by "number" and then by the zero-indexed column:
usort($data, make_comparer('number', 0));
See it in action.
Advanced features
More advanced features are available if you specify a sort column as an array instead of a simple string. This array should be numerically indexed, and must contain these items:
0 => the column name to sort on (mandatory)
1 => either SORT_ASC or SORT_DESC (optional)
2 => a projection function (optional)
Let's see how we can use these features.
Reverse sort
To sort by name descending:
usort($data, make_comparer(['name', SORT_DESC]));
See it in action.
To sort by number descending and then by name descending:
usort($data, make_comparer(['number', SORT_DESC], ['name', SORT_DESC]));
See it in action.
Custom projections
In some scenarios you may need to sort by a column whose values do not lend well to sorting. The "birthday" column in the sample data set fits this description: it does not make sense to compare birthdays as strings (because e.g. "01/01/1980" comes before "10/10/1970"). In this case we want to specify how to project the actual data to a form that can be compared directly with the desired semantics.
Projections can be specified as any type of callable: as strings, arrays, or anonymous functions. A projection is assumed to accept one argument and return its projected form.
It should be noted that while projections are similar to the custom comparison functions used with usort and family, they are simpler (you only need to convert one value to another) and take advantage of all the functionality already baked into make_comparer.
Let's sort the example data set without a projection and see what happens:
usort($data, make_comparer('birthday'));
See it in action.
That was not the desired outcome. But we can use date_create as a projection:
usort($data, make_comparer(['birthday', SORT_ASC, 'date_create']));
See it in action.
This is the correct order that we wanted.
There are many more things that projections can achieve. For example, a quick way to get a case-insensitive sort is to use strtolower as a projection.
That said, I should also mention that it's better to not use projections if your data set is large: in that case it would be much faster to project all your data manually up front and then sort without using a projection, although doing so will trade increased memory usage for faster sort speed.
Finally, here is an example that uses all the features: it first sorts by number descending, then by birthday ascending:
usort($data, make_comparer(
['number', SORT_DESC],
['birthday', SORT_ASC, 'date_create']
));
See it in action.
You can use array_multisort()
Try something like this:
foreach ($mdarray as $key => $row) {
// replace 0 with the field's index/key
$dates[$key] = $row[0];
}
array_multisort($dates, SORT_DESC, $mdarray);
For PHP >= 5.5.0 just extract the column to sort by. No need for the loop:
array_multisort(array_column($mdarray, 0), SORT_DESC, $mdarray);
With usort. Here's a generic solution, that you can use for different columns:
class TableSorter {
protected $column;
function __construct($column) {
$this->column = $column;
}
function sort($table) {
usort($table, array($this, 'compare'));
return $table;
}
function compare($a, $b) {
if ($a[$this->column] == $b[$this->column]) {
return 0;
}
return ($a[$this->column] < $b[$this->column]) ? -1 : 1;
}
}
To sort by first column:
$sorter = new TableSorter(0); // sort by first column
$mdarray = $sorter->sort($mdarray);
Multiple row sorting using a closure
Here's another approach using uasort() and an anonymous callback function (closure). I've used that function regularly. PHP 5.3 required – no more dependencies!
/**
* Sorting array of associative arrays - multiple row sorting using a closure.
* See also: http://the-art-of-web.com/php/sortarray/
*
* #param array $data input-array
* #param string|array $fields array-keys
* #license Public Domain
* #return array
*/
function sortArray( $data, $field ) {
$field = (array) $field;
uasort( $data, function($a, $b) use($field) {
$retval = 0;
foreach( $field as $fieldname ) {
if( $retval == 0 ) $retval = strnatcmp( $a[$fieldname], $b[$fieldname] );
}
return $retval;
} );
return $data;
}
/* example */
$data = array(
array( "firstname" => "Mary", "lastname" => "Johnson", "age" => 25 ),
array( "firstname" => "Amanda", "lastname" => "Miller", "age" => 18 ),
array( "firstname" => "James", "lastname" => "Brown", "age" => 31 ),
array( "firstname" => "Patricia", "lastname" => "Williams", "age" => 7 ),
array( "firstname" => "Michael", "lastname" => "Davis", "age" => 43 ),
array( "firstname" => "Sarah", "lastname" => "Miller", "age" => 24 ),
array( "firstname" => "Patrick", "lastname" => "Miller", "age" => 27 )
);
$data = sortArray( $data, 'age' );
$data = sortArray( $data, array( 'lastname', 'firstname' ) );
I know it's 2 years since this question was asked and answered, but here's another function that sorts a two-dimensional array. It accepts a variable number of arguments, allowing you to pass in more than one key (ie column name) to sort by. PHP 5.3 required.
function sort_multi_array ($array, $key)
{
$keys = array();
for ($i=1;$i<func_num_args();$i++) {
$keys[$i-1] = func_get_arg($i);
}
// create a custom search function to pass to usort
$func = function ($a, $b) use ($keys) {
for ($i=0;$i<count($keys);$i++) {
if ($a[$keys[$i]] != $b[$keys[$i]]) {
return ($a[$keys[$i]] < $b[$keys[$i]]) ? -1 : 1;
}
}
return 0;
};
usort($array, $func);
return $array;
}
Try it here: http://www.exorithm.com/algorithm/view/sort_multi_array
You can sort an array using usort function.
$array = array(
array('price'=>'1000.50','product'=>'product 1'),
array('price'=>'8800.50','product'=>'product 2'),
array('price'=>'200.0','product'=>'product 3')
);
function cmp($a, $b) {
return $a['price'] > $b['price'];
}
usort($array, "cmp");
print_r($array);
Output :
Array
(
[0] => Array
(
[price] => 134.50
[product] => product 1
)
[1] => Array
(
[price] => 2033.0
[product] => product 3
)
[2] => Array
(
[price] => 8340.50
[product] => product 2
)
)
Example
Here is a php4/php5 class that will sort one or more fields:
// a sorter class
// php4 and php5 compatible
class Sorter {
var $sort_fields;
var $backwards = false;
var $numeric = false;
function sort() {
$args = func_get_args();
$array = $args[0];
if (!$array) return array();
$this->sort_fields = array_slice($args, 1);
if (!$this->sort_fields) return $array();
if ($this->numeric) {
usort($array, array($this, 'numericCompare'));
} else {
usort($array, array($this, 'stringCompare'));
}
return $array;
}
function numericCompare($a, $b) {
foreach($this->sort_fields as $sort_field) {
if ($a[$sort_field] == $b[$sort_field]) {
continue;
}
return ($a[$sort_field] < $b[$sort_field]) ? ($this->backwards ? 1 : -1) : ($this->backwards ? -1 : 1);
}
return 0;
}
function stringCompare($a, $b) {
foreach($this->sort_fields as $sort_field) {
$cmp_result = strcasecmp($a[$sort_field], $b[$sort_field]);
if ($cmp_result == 0) continue;
return ($this->backwards ? -$cmp_result : $cmp_result);
}
return 0;
}
}
/////////////////////
// usage examples
// some starting data
$start_data = array(
array('first_name' => 'John', 'last_name' => 'Smith', 'age' => 10),
array('first_name' => 'Joe', 'last_name' => 'Smith', 'age' => 11),
array('first_name' => 'Jake', 'last_name' => 'Xample', 'age' => 9),
);
// sort by last_name, then first_name
$sorter = new Sorter();
print_r($sorter->sort($start_data, 'last_name', 'first_name'));
// sort by first_name, then last_name
$sorter = new Sorter();
print_r($sorter->sort($start_data, 'first_name', 'last_name'));
// sort by last_name, then first_name (backwards)
$sorter = new Sorter();
$sorter->backwards = true;
print_r($sorter->sort($start_data, 'last_name', 'first_name'));
// sort numerically by age
$sorter = new Sorter();
$sorter->numeric = true;
print_r($sorter->sort($start_data, 'age'));
Before I could get the TableSorter class to run I had came up with a function based on what Shinhan had provided.
function sort2d_bycolumn($array, $column, $method, $has_header)
{
if ($has_header) $header = array_shift($array);
foreach ($array as $key => $row) {
$narray[$key] = $row[$column];
}
array_multisort($narray, $method, $array);
if ($has_header) array_unshift($array, $header);
return $array;
}
$array is the MD Array you want to sort.
$column is the column you wish to sort by.
$method is how you want the sort performed, such as SORT_DESC
$has_header is set to true if the first row contains header values that you don't want sorted.
The "Usort" function is your answer.
http://php.net/usort
I tried several popular array_multisort() and usort() answers and none of them worked for me. The data just gets jumbled and the code is unreadable. Here's a quick a dirty solution. WARNING: Only use this if you're sure a rogue delimiter won't come back to haunt you later!
Let's say each row in your multi array looks like: name, stuff1, stuff2:
// Sort by name, pull the other stuff along for the ride
foreach ($names_stuff as $name_stuff) {
// To sort by stuff1, that would be first in the contatenation
$sorted_names[] = $name_stuff[0] .','. name_stuff[1] .','. $name_stuff[2];
}
sort($sorted_names, SORT_STRING);
Need your stuff back in alphabetical order?
foreach ($sorted_names as $sorted_name) {
$name_stuff = explode(',',$sorted_name);
// use your $name_stuff[0]
// use your $name_stuff[1]
// ...
}
Yeah, it's dirty. But super easy, won't make your head explode.

Categories