140

I have the following array structure:

Array
        (
            [0] => Array
                (
                    [configuration_id] => 10
                    [id] => 1
                    [optionNumber] => 3
                    [optionActive] => 1
                    [lastUpdated] => 2010-03-17 15:44:12
                )

            [1] => Array
                (
                    [configuration_id] => 9
                    [id] => 1
                    [optionNumber] => 2
                    [optionActive] => 1
                    [lastUpdated] => 2010-03-17 15:44:12
                )

            [2] => Array
                (
                    [configuration_id] => 8
                    [id] => 1
                    [optionNumber] => 1
                    [optionActive] => 1
                    [lastUpdated] => 2010-03-17 15:44:12
                )
    )

What is the best way to order the array in an incremental way, based on the optionNumber?

So the results look like:

Array
        (
            [0] => Array
                (
                    [configuration_id] => 8
                    [id] => 1
                    [optionNumber] => 1
                    [optionActive] => 1
                    [lastUpdated] => 2010-03-17 15:44:12
                )

            [1] => Array
                (
                    [configuration_id] => 9
                    [id] => 1
                    [optionNumber] => 2
                    [optionActive] => 1
                    [lastUpdated] => 2010-03-17 15:44:12
                )

            [2] => Array
                (
                    [configuration_id] => 10
                    [id] => 1
                    [optionNumber] => 3
                    [optionActive] => 1
                    [lastUpdated] => 2010-03-17 15:44:12
                )
    )
jla
  • 4,191
  • 3
  • 27
  • 44
Sjwdavies
  • 4,079
  • 7
  • 38
  • 43

8 Answers8

249

Use usort.

function cmp_by_optionNumber($a, $b) {
  return $a["optionNumber"] - $b["optionNumber"];
}

...

usort($array, "cmp_by_optionNumber");

In PHP ≥5.3, you should use an anonymous function instead:

usort($array, function ($a, $b) {
    return $a['optionNumber'] - $b['optionNumber'];
});

Note that both code above assume $a['optionNumber'] is an integer. Use @St. John Johnson's solution if they are strings.


In PHP ≥7.0, use the spaceship operator <=> instead of subtraction to prevent overflow/truncation problems.

usort($array, function ($a, $b) {
    return $a['optionNumber'] <=> $b['optionNumber'];
});
Community
  • 1
  • 1
kennytm
  • 510,854
  • 105
  • 1,084
  • 1,005
  • 1
    That doesn't really helpe me as usort requires I provide it a function to use - which is the difficult bit I can't get my head round – Sjwdavies Mar 19 '10 at 13:15
  • 19
    Well he just gave you the function to use. And you're going to have to accept that there's not always a built-in function to do what you want, you have to write it yourself. Comparison functions just require a return of 1, 0, or -1 indicating the sort order for two elements. – Tesserex Mar 19 '10 at 13:19
  • 1
    I looked further into usort and it is actually quite cool. I wrote a simple comparison function to the one above, however missed out the '=='. Thanks for the help guys – Sjwdavies Mar 19 '10 at 13:38
  • Not totally sure, but I believe that if you have floats here you might run into trouble. For example, if a was 3 and b was 2.5 they would be compared as equal. – Andrew Oct 18 '13 at 17:38
  • 3
    Now also as closure:-- usort($array, function($a,$b){ return $b["optionNumber"] - $a["optionNumber"]; }); – Joeri Dec 03 '14 at 20:34
  • `return $a["optionNumber"] - $b["optionNumber"]` does not sort floats correctly, it should be `return $a["optionNumber"] < $b["optionNumber"] ? -1 : 1` as the return value is casted to int by `usort` – Christian Feb 24 '16 at 13:05
  • In their suggested edit [gorillagoat](https://stackoverflow.com/users/6683084/gorillagoat) added: Use `uksort()` to keep your original keys intact. – YakovL Oct 05 '17 at 16:12
  • Thanks for the solution. That work for me. But i can't understand how that's works. I'm in PHP 5.6, then i used anonymous function. but how substract a with b can sort my array :O – Kiloumap Mrz Apr 09 '18 at 16:04
  • 1
    @KiloumapL'artélon If the result is `< 0`, it tells the sort function that `a` should appear before `b`. If it is `> 0` then `b` should appear before `a`. – kennytm Apr 10 '18 at 16:19
  • @kennytm How can i do the two time sorting with different key? – Bhavin Thummar Jan 10 '19 at 15:09
  • 1
    And to those who want to sort by DESC, just switch `$a` and `$b` – NicklasF Jan 15 '20 at 20:41
  • 1
    Best solution imo, only difference is that I use `uasort()` instead of `usort()` to keep keys :) – rAthus May 17 '22 at 14:05
  • usort on sub array value may affect on output buffering by ob_start – dwi kristianto Mar 07 '23 at 05:21
66

Use usort

 usort($array, 'sortByOption');
 function sortByOption($a, $b) {
   return strcmp($a['optionNumber'], $b['optionNumber']);
 }
St. John Johnson
  • 6,590
  • 7
  • 35
  • 56
  • 8
    @BenSinclair, that's because Kenny's solution is for numbers, this solution is for strings. They are both correct :-) +1 for this alternative. – kubilay Jan 29 '13 at 19:34
  • For case insensitive sort use strcasecmp instead of strcmp – user570605 Mar 02 '18 at 14:57
  • can we define key for second order in array means we do first sorting with optionNumber then sorting with lastUpdated. How can do this thing? – Bhavin Thummar Jan 10 '19 at 15:06
19

I used both solutions by KennyTM and AJ Quick and came up with a function that can help in this issue for many cases like using ASC or DESC sorting or preserving keys or if you have objects as children of array.

Here is this function (works for PHP7 and higher because of spaceship operator):

/**
 * @param array $array
 * @param string $value
 * @param bool $asc - ASC (true) or DESC (false) sorting
 * @param bool $preserveKeys
 * @return array
 * */
function sortBySubValue($array, $value, $asc = true, $preserveKeys = false)
{
    if ($preserveKeys) {
        $c = [];
        if (is_object(reset($array))) {
            foreach ($array as $k => $v) {
                $b[$k] = strtolower($v->$value);
            }
        } else {
            foreach ($array as $k => $v) {
                $b[$k] = strtolower($v[$value]);
            }
        }
        $asc ? asort($b) : arsort($b);
        foreach ($b as $k => $v) {
            $c[$k] = $array[$k];
        }
        $array = $c;
    } else {
        if (is_object(reset($array))) {
            usort($array, function ($a, $b) use ($value, $asc) {
                return $a->{$value} == $b->{$value} ? 0 : ($a->{$value} <=> $b->{$value}) * ($asc ? 1 : -1);
            });
        } else {
            usort($array, function ($a, $b) use ($value, $asc) {
                return $a[$value] == $b[$value] ? 0 : ($a[$value] <=> $b[$value]) * ($asc ? 1 : -1);
            });
        }
    }

    return $array;
}

Usage:

sortBySubValue($array, 'optionNumber', true, false);

Edit

The first part can be rewritten using uasort() and the function will be shorter (works for PHP7 and higher because of spaceship operator):

/**
 * @param array $array
 * @param string $value
 * @param bool $asc - ASC (true) or DESC (false) sorting
 * @param bool $preserveKeys
 * @return array
 * */
function sortBySubValue($array, $value, $asc = true, $preserveKeys = false)
{
    if (is_object(reset($array))) {
        $preserveKeys ? uasort($array, function ($a, $b) use ($value, $asc) {
            return $a->{$value} == $b->{$value} ? 0 : ($a->{$value} <=> $b->{$value}) * ($asc ? 1 : -1);
        }) : usort($array, function ($a, $b) use ($value, $asc) {
            return $a->{$value} == $b->{$value} ? 0 : ($a->{$value} <=> $b->{$value}) * ($asc ? 1 : -1);
        });
    } else {
        $preserveKeys ? uasort($array, function ($a, $b) use ($value, $asc) {
            return $a[$value] == $b[$value] ? 0 : ($a[$value] <=> $b[$value]) * ($asc ? 1 : -1);
        }) : usort($array, function ($a, $b) use ($value, $asc) {
            return $a[$value] == $b[$value] ? 0 : ($a[$value] <=> $b[$value]) * ($asc ? 1 : -1);
        });
    }
    return $array;
}
Pigalev Pavel
  • 1,155
  • 1
  • 15
  • 29
  • 1
    To make this work for me, I had to use `>` (greater than) instead of `-` (minus) when comparing `$a` and `$b` values since I was comparing strings. Still works though. – James Nov 13 '15 at 15:19
  • 1
    @James you are right. I changed the answer and added the use of spaceship operator (<=>). Now it should work just fine. – Pigalev Pavel Sep 19 '18 at 10:04
  • Is there a way to make this case insensitive? – loeffel Nov 04 '19 at 11:53
18

Using array_multisort(), array_map()

array_multisort(array_map(function($element) {
      return $element['optionNumber'];
  }, $array), SORT_ASC, $array);

print_r($array);

DEMO

Ghanshyam Nakiya
  • 1,602
  • 17
  • 24
4

The keys are removed when using a function like the ones above. If the keys are important, the following function would maintain it... but foreach loops are pretty inefficient.

function subval_sort($a,$subkey) {
    foreach($a as $k=>$v) {
        $b[$k] = strtolower($v[$subkey]);
    }
    asort($b);
    foreach($b as $key=>$val) {
        $c[$key] = $a[$key];
    }
    return $c;
}
$array = subval_sort($array,'optionNumber');

Use arsort instead of asort if you want from high to low.

Code credit: http://www.firsttube.com/read/sorting-a-multi-dimensional-array-with-php/

AJ Quick
  • 161
  • 14
3

PHP 5.3+

usort($array, function($a,$b){ return $a['optionNumber']-$b['optionNumber'];} );
Samer Ata
  • 1,027
  • 1
  • 12
  • 11
3

The two most modern, most concise approaches are:

  1. usort() with arrow function syntax and a spaceship (3-way comparison) operator. (Demo)

    usort($array, fn($a, $b) =>
        $a['optionNumber'] <=> $b['optionNumber']
    );
    

    $a <=> $b gives ascending sorting; $b <=> $a gives descending sorting.

  2. array_multisort() with an array_column() call to isolate the value to compare. (Demo)

    array_multisort(
        array_column($array, 'optionNumber'),
        $array
    );
    

    It is not necessary to include the sorting direction flag because ascending is the default/implied direction when omitted.


Both approaches above require the targeted array column to exist in all rows, or the approach will fail/break/error.

mickmackusa
  • 43,625
  • 12
  • 83
  • 136
0

A one-line solution using array_multisort and array_column.

//your array
$yourarray = Array
          (
           "0" => Array
                  (
                    "configuration_id" => 10,
                    "id" => 1,
                    "optionNumber" => 3,
                    "optionActive" => 1,
                    "lastUpdated" => "2010-03-17 15:44:12"
                  ),
           "1" => Array
                  (
                    "configuration_id" => 9,
                    "id" => 1,
                    "optionNumber" => 2,
                    "optionActive" => 1,
                    "lastUpdated" => "2010-03-17 15:44:12"
                  ),
           "2" => Array
                  (
                    "configuration_id" => 8,
                    "id" => 1,
                    "optionNumber" => 1,
                    "optionActive" => 1,
                    "lastUpdated" => "2010-03-17 15:44:12"
                  )
);

//access optionNumber in the child arrays using array_column
array_multisort(array_column($yourarray, 'optionNumber'), SORT_ASC, $yourarray);

//print out preformatted
echo "<pre>"; print_r($images); echo "</pre>";
garrettlynchirl
  • 790
  • 8
  • 23