Matt
Matt

Reputation: 22911

Sort an array of associative arrays by column value

Given this array:

$inventory = array(

   array("type"=>"fruit", "price"=>3.50),
   array("type"=>"milk", "price"=>2.90),
   array("type"=>"pork", "price"=>5.43),

);

I would like to sort $inventory's elements by price to get:

$inventory = array(

   array("type"=>"pork", "price"=>5.43),
   array("type"=>"fruit", "price"=>3.50),
   array("type"=>"milk", "price"=>2.90),

);

How can I do this?

Upvotes: 616

Views: 767547

Answers (23)

Abir Husain
Abir Husain

Reputation: 87

Many people are searching for a way to do this with Laravel and are ending up here. Also, some Laravel questions are getting closed due to duplicates to this question.

Hence, I shared an easy way to perform it with the Laravel collect() method.

$inventory = collect($inventory)->sortBy('price')->toArray();

For Descending order

$inventory = collect($inventory)->sortBy('price')->reverse()->toArray();

Or,

$inventory = collect($inventory)->('price')->reverse()->toArray();

Upvotes: 3

Syscall
Syscall

Reputation: 19780

As of PHP 7.4, you can use the arrow function:

usort(
    $inventory, 
    fn(array $a, array $b): int => $b['price'] <=> $a['price']
);

Code (demo):

$inventory = [
    ['type' => 'fruit', 'price' => 3.50],
    ['type' => 'milk',  'price' => 2.90],
    ['type' => 'pork',  'price' => 5.43],
];

usort(
    $inventory, 
    fn(array $a, array $b): int => $b['price'] <=> $a['price']
);

print_r($inventory);

(Condensed) output:

Array
(
    [0] => Array ([type] => pork,  [price] => 5.43)
    [1] => Array ([type] => fruit, [price] => 3.5)
    [2] => Array ([type] => milk,  [price] => 2.9)
)

Upvotes: 7

mirado
mirado

Reputation: 99

I use uasort like this:

<?php
$users = [
    [
        'username' => 'joe',
        'age' => 11
    ],
    [
        'username' => 'rakoto',
        'age' => 21
    ],
    [
        'username' => 'rabe',
        'age' => 17
    ],
    [
        'username' => 'fy',
        'age' => 19
    ],
];


uasort($users, function ($item, $compare) {
    return $item['username'] >= $compare['username'];
});

var_dump($users);

Upvotes: 6

Ahmad Sayeed
Ahmad Sayeed

Reputation: 354

Complete Dynamic Function

I jumped here for associative array sorting and found this amazing function on sort. This function is very dynamic and sort in ascending and descending order with a specified key.

A simple function to sort an array by a specific key.

It maintains the index association.

<?php

function array_sort($array, $on, $order=SORT_ASC)
{
    $new_array = array();
    $sortable_array = array();

    if (count($array) > 0) {
        foreach ($array as $k => $v) {
            if (is_array($v)) {
                foreach ($v as $k2 => $v2) {
                    if ($k2 == $on) {
                        $sortable_array[$k] = $v2;
                    }
                }
            } else {
                $sortable_array[$k] = $v;
            }
        }

        switch ($order) {
            case SORT_ASC:
                asort($sortable_array);
            break;
            case SORT_DESC:
                arsort($sortable_array);
            break;
        }

        foreach ($sortable_array as $k => $v) {
            $new_array[$k] = $array[$k];
        }
    }

    return $new_array;
}

$people = array(
    12345 => array(
        'id' => 12345,
        'first_name' => 'Joe',
        'surname' => 'Bloggs',
        'age' => 23,
        'sex' => 'm'
    ),
    12346 => array(
        'id' => 12346,
        'first_name' => 'Adam',
        'surname' => 'Smith',
        'age' => 18,
        'sex' => 'm'
    ),
    12347 => array(
        'id' => 12347,
        'first_name' => 'Amy',
        'surname' => 'Jones',
        'age' => 21,
        'sex' => 'f'
    )
);

print_r(array_sort($people, 'age', SORT_DESC)); // Sort by oldest first
print_r(array_sort($people, 'surname', SORT_ASC)); // Sort by surname

Upvotes: 0

kenorb
kenorb

Reputation: 166419

You can use usort with an anonymous function, e.g.,

usort($inventory, function ($a, $b) { return strnatcmp($a['price'], $b['price']); });

Upvotes: 21

jamshid
jamshid

Reputation: 333

Try this:

$prices = array_column($inventory, 'price');
array_multisort($prices, SORT_DESC, $inventory);
print_r($inventory);

Upvotes: 4

mpen
mpen

Reputation: 282875

This function is reusable:

function usortarr(&$array, $key, $callback = 'strnatcasecmp') {
    uasort($array, function($a, $b) use($key, $callback) {
        return call_user_func($callback, $a[$key], $b[$key]);
    });
}

It works well on string values by default, but you'll have to sub the callback for a number comparison function if all your values are numbers.

Upvotes: 3

Kamal
Kamal

Reputation: 794

From Sort an array of associative arrays by value of given key in PHP:

By using usort, we can sort an array in ascending and descending order. We just need to create a function and pass it as parameter in usort. As per below example used greater than for ascending order if we passed less than condition then it's sort in descending order.

Example:

$array = array(
  array('price' => '1000.50', 'product' => 'test1'),
  array('price' => '8800.50', 'product' => 'test2'),
  array('price' => '200.0'  , 'product' => 'test3')
);

function cmp($a, $b) {
  return $a['price'] > $b['price'];
}

usort($array, "cmp");
print_r($array);

Output:

Array
 (
    [0] => Array
        (
            [price] => 200.0
            [product] => test3
        )

    [1] => Array
        (
            [price] => 1000.50
            [product] => test1
        )

    [2] => Array
        (
            [price] => 8800.50
            [product] => test2
        )
  )

Upvotes: 17

Nefelim
Nefelim

Reputation: 95

This was tested on 100,000 records:

Time in seconds (calculated by function microtime). Only for unique values on sorting key positions.

Solution of function of @Josh Davis:

Spent time: 1.58

My solution:

Spent time: 0.094

Solution:

function SortByKeyValue($data, $sortKey, $sort_flags=SORT_ASC)
{
    if (empty($data) or empty($sortKey))
        return $data;

    $ordered = array();
    foreach ($data as $key => $value)
        $ordered[$value[$sortKey]] = $value;

    ksort($ordered, $sort_flags);

    return array_values($ordered); *// array_values() added for identical result with multisort*
}

Upvotes: 6

Josh Davis
Josh Davis

Reputation: 28730

You are right; the function you're looking for is array_multisort().

Here's an example taken straight from the manual and adapted to your case:

$price = array();
foreach ($inventory as $key => $row)
{
    $price[$key] = $row['price'];
}
array_multisort($price, SORT_DESC, $inventory);

As of PHP 5.5.0, you can use array_column() instead of that foreach:

$price = array_column($inventory, 'price');

array_multisort($price, SORT_DESC, $inventory);

Upvotes: 806

Arda Basoglu
Arda Basoglu

Reputation: 1600

/**
 * Sorts an associative array by a specific key and direction.
 *
 * @param array $array The associative array to sort.
 * @param string $key The key to sort by.
 * @param string $direction The sort direction ("ASC" for ascending, "DESC" for descending).
 * @return array The sorted associative array.
 */
function sortAssociativeArrayByKey($array, $key, $direction) {
    usort($array, function ($a, $b) use ($key, $direction) {
        $comparison = $a[$key] <=> $b[$key];
        return ($direction === 'DESC') ? -$comparison : $comparison;
    });
    return $array;
}

Upvotes: 8

Ejaz UL Haq
Ejaz UL Haq

Reputation: 81

This function works 100% on all major versions of PHP and it is tested with PHP5, PHP7, PHP8.

    function sort_my_array($array, $order_by, $order)
    {
        switch ($order) {
            case "asc":
                usort($array, function ($first, $second) use ($order_by) {
                    if (version_compare(PHP_VERSION, '7.0.0') >= 0) {
                        return $first[$order_by] <=> $second[$order_by];
                    } else {
                        $array_cmp = strcmp($first[$order_by], $second[$order_by]);
                        return $array_cmp ;
                    }
                });
                break;
            case "desc":
                usort($certificates, function ($first, $second) use ($order_by) {
                    if (version_compare(PHP_VERSION, '7.0.0') >= 0) {
                        return $first[$order_by] <=> $second[$order_by];
                    } else {
                        $array_cmp = strcmp($first[$order_by], $second[$order_by]);
                        return -$array_cmp ;
                    }
                });
                break;
            default:
                break;
        }
        return $array;
    }

Upvotes: -2

Mariano Iglesias
Mariano Iglesias

Reputation: 1937

While others have correctly suggested the use of array_multisort(), for some reason no answer seems to acknowledge the existence of array_column(), which can greatly simplify the solution. So my suggestion would be:

array_multisort(array_column($inventory, 'price'), SORT_DESC, $inventory);

If you want Case Insensitive Sort on strings, you can use SORT_NATURAL|SORT_FLAG_CASE

array_multisort(array_column($inventory, 'key_name'), SORT_DESC, SORT_NATURAL|SORT_FLAG_CASE, $inventory);

Upvotes: 165

Jason Campbell
Jason Campbell

Reputation: 1

If you need to sort an array of strings with different cases, this will change the sorting array values to lowercase.

$data = [
    [
        'name' => 'jack',
        'eyeColor' => 'green'
    ],
    [
        'name' => 'Amy',
        'eyeColor' => 'brown'
    ],
    [   
        'name' => 'Cody',
        'eyeColor' => 'blue'
    ] 
];
function toLowerCase($a) { return strtolower($a); }
$sortArray = array_map("toLowerCase",array_column($data, 'name'));
array_multisort($sortArray, SORT_ASC, $data);

Upvotes: -1

LeviZoesch
LeviZoesch

Reputation: 1621

Here is a method that I found long ago and cleaned up a bit. This works great, and can be quickly changed to accept objects as well.

/**
 * A method for sorting arrays by a certain key:value.
 * SortByKey is the key you wish to sort by
 * Direction can be ASC or DESC.
 *
 * @param $array
 * @param $sortByKey
 * @param $sortDirection
 * @return array
 */
private function sortArray($array, $sortByKey, $sortDirection) {

    $sortArray = array();
    $tempArray = array();

    foreach ( $array as $key => $value ) {
        $tempArray[] = strtolower( $value[ $sortByKey ] );
    }

    if($sortDirection=='ASC'){ asort($tempArray ); }
        else{ arsort($tempArray ); }

    foreach ( $tempArray as $key => $temp ){
        $sortArray[] = $array[ $key ];
    }

    return $sortArray;

}

to change the method to sort objects simply change the following line:

$tempArray[] = strtolower( $value[ $sortByKey ] ); to $tempArray[] = strtolower( $value->$sortByKey );

To run the method simply do

sortArray($inventory,'price','ASC');

Upvotes: 2

Mark Amery
Mark Amery

Reputation: 154715

PHP 7+

As of PHP 7, this can be done concisely using usort with an anonymous function that uses the spaceship operator to compare elements.

You can do an ascending sort like this:

usort($inventory, function ($item1, $item2) {
    return $item1['price'] <=> $item2['price'];
});

Or a descending sort like this:

usort($inventory, function ($item1, $item2) {
    return $item2['price'] <=> $item1['price'];
});

To understand how this works, note that usort takes a user-provided comparison function that must behave as follows (from the docs):

The comparison function must return an integer less than, equal to, or greater than zero if the first argument is considered to be respectively less than, equal to, or greater than the second.

And note also that <=>, the spaceship operator,

returns 0 if both operands are equal, 1 if the left is greater, and -1 if the right is greater

which is exactly what usort needs. In fact, almost the entire justification given for adding <=> to the language in https://wiki.php.net/rfc/combined-comparison-operator is that it

makes writing ordering callbacks for use with usort() easier


PHP 5.3+

PHP 5.3 introduced anonymous functions, but doesn't yet have the spaceship operator. We can still use usort to sort our array, but it's a little more verbose and harder to understand:

usort($inventory, function ($item1, $item2) {
    if ($item1['price'] == $item2['price']) return 0;
    return $item1['price'] < $item2['price'] ? -1 : 1;
});

Note that although it's fairly common for comparators dealing with integer values to just return the difference of the values, like $item2['price'] - $item1['price'], we can't safely do that in this case. This is because the prices are floating point numbers in the question asker's example, but the comparison function we pass to usort has to return integers for usort to work properly:

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.

This is an important trap to bear in mind when using usort in PHP 5.x! My original version of this answer made this mistake and yet I accrued ten upvotes over thousands of views apparently without anybody noticing the serious bug. The ease with which lackwits like me can screw up comparator functions is precisely the reason that the easier-to-use spaceship operator was added to the language in PHP 7.

Upvotes: 519

Kamal
Kamal

Reputation: 794

//Just in one line custom function
function cmp($a, $b)
{
return (float) $a['price'] < (float)$b['price'];
}
@uasort($inventory, "cmp");
print_r($inventory);

//result

Array
(
[2] => Array
    (
        [type] => pork
        [price] => 5.43
    )

[0] => Array
    (
        [type] => fruit
        [price] => 3.5
    )

[1] => Array
    (
        [type] => milk
        [price] => 2.9
    )

)

Upvotes: 0

Chirag Pipariya
Chirag Pipariya

Reputation: 441

$arr1 = array(

    array('id'=>1,'name'=>'aA','cat'=>'cc'),
    array('id'=>2,'name'=>'aa','cat'=>'dd'),
    array('id'=>3,'name'=>'bb','cat'=>'cc'),
    array('id'=>4,'name'=>'bb','cat'=>'dd')
);

$result1 = array_msort($arr1, array('name'=>SORT_DESC);

$result2 = array_msort($arr1, array('cat'=>SORT_ASC);

$result3 = array_msort($arr1, array('name'=>SORT_DESC, 'cat'=>SORT_ASC));


function array_msort($array, $cols)
{
    $colarr = array();
    foreach ($cols as $col => $order) {
    $colarr[$col] = array();
    foreach ($array as $k => $row) { $colarr[$col]['_'.$k] = strtolower($row[$col]); }
}

$eval = 'array_multisort(';

foreach ($cols as $col => $order) {
    $eval .= '$colarr[\''.$col.'\'],'.$order.',';
}

$eval = substr($eval,0,-1).');';
eval($eval);
$ret = array();
foreach ($colarr as $col => $arr) {
    foreach ($arr as $k => $v) {
        $k = substr($k,1);
        if (!isset($ret[$k])) $ret[$k] = $array[$k];
        $ret[$k][$col] = $array[$k][$col];
    }
}
return $ret;


} 

Upvotes: -2

Danielzt
Danielzt

Reputation: 503

I ended on this:

function sort_array_of_array(&$array, $subfield)
{
    $sortarray = array();
    foreach ($array as $key => $row)
    {
        $sortarray[$key] = $row[$subfield];
    }

    array_multisort($sortarray, SORT_ASC, $array);
}

Just call the function, passing the array and the name of the field of the second level array. Like:

sort_array_of_array($inventory, 'price');

Upvotes: 30

sarsnake
sarsnake

Reputation: 27713

try this:

asort($array_to_sort, SORT_NUMERIC);

for reference see this: http://php.net/manual/en/function.asort.php

see various sort flags here: http://www.php.net/manual/en/function.sort.php

Upvotes: -7

danamlund
danamlund

Reputation: 674

$inventory = 
    array(array("type"=>"fruit", "price"=>3.50),
          array("type"=>"milk", "price"=>2.90),
          array("type"=>"pork", "price"=>5.43),
          );

function pricesort($a, $b) {
  $a = $a['price'];
  $b = $b['price'];
  if ($a == $b)
    return 0;
  return ($a > $b) ? -1 : 1;
}

usort($inventory, "pricesort");
// uksort($inventory, "pricesort");

print("first: ".$inventory[0]['type']."\n\n");
// for usort(): prints milk (item with lowest price)
// for uksort(): prints fruit (item with key 0 in the original $inventory)

// foreach prints the same for usort and uksort.
foreach($inventory as $i){
  print($i['type'].": ".$i['price']."\n");
}

outputs:

first: pork

pork: 5.43
fruit: 3.5
milk: 2.9

Upvotes: 13

Alex Sexton
Alex Sexton

Reputation: 10451

You might try to define your own comparison function and then use usort.

Upvotes: 1

zombat
zombat

Reputation: 94167

Since your array elements are arrays themselves with string keys, your best bet is to define a custom comparison function. It's pretty quick and easy to do. Try this:

function invenDescSort($item1,$item2)
{
    if ($item1['price'] == $item2['price']) return 0;
    return ($item1['price'] < $item2['price']) ? 1 : -1;
}
usort($inventory,'invenDescSort');
print_r($inventory);

Produces the following:

Array
(
    [0] => Array
        (
            [type] => pork
            [price] => 5.43
        )

    [1] => Array
        (
            [type] => fruit
            [price] => 3.5
        )

    [2] => Array
        (
            [type] => milk
            [price] => 2.9
        )

)

Upvotes: 47

Related Questions