coudenysj Jachim Coudenys on web technology

25Sep/0941

php msort() – multidimensional array sort

There have been several moments where I had a rowset from a database as a PHP array, and I had to sort it based on some columns.

$tickets = array(
    array(
        
'id' => 13,
        
'owner' => 'jachim',
        
'time' => '2009-09-25 10:39:42.011612',
        
'project' => 'jachim.be',
        
'title' => 'Some random ticket'
    
),
    array(
        
'id' => 31,
        
'owner' => 'jachim',
        
'time' => '2009-09-24 14:38:47.945020',
        
'project' => 'joggink.be',
        
'title' => 'Some other random ticket'
    
),
    array(
        
'id' => 22,
        
'owner' => 'root',
        
'time' => '2009-09-24 10:58:02.904198',
        
'project' => 'joggink.be',
        
'title' => 'A specific ticket'
    
)
);

I searched for functions for a while, and even try to understand/use the array_multisort function, but I never managed to get something working on a simple array (like the one above). The new function msort() should be a solution for this.

The function works normally when you use a string for the second parameter. That way the $sort_flag works like you would expect it. When using an array of keys however, a string key is built to sort the array on. This part could use some improvement.

Here an example of the usage:


var_dump($tickets);

$tickets msort($tickets, array('owner''time'));

var_dump($tickets);

And here's the function itself:

/**
 * Sort a 2 dimensional array based on 1 or more indexes.
 * 
 * msort() can be used to sort a rowset like array on one or more
 * 'headers' (keys in the 2th array).
 * 
 * @param array        $array      The array to sort.
 * @param string|array $key        The index(es) to sort the array on.
 * @param int          $sort_flags The optional parameter to modify the sorting 
 *                                 behavior. This parameter does not work when 
 *                                 supplying an array in the $key parameter. 
 * 
 * @return array The sorted array.
 */
function msort($array$key$sort_flags SORT_REGULAR) {
    if (
is_array($array) && count($array) > 0) {
        if (!empty(
$key)) {
            
$mapping = array();
            foreach (
$array as $k => $v) {
                
$sort_key '';
                if (!
is_array($key)) {
                    
$sort_key $v[$key];
                } else {
                    
// @TODO This should be fixed, now it will be sorted as string
                    
foreach ($key as $key_key) {
                        
$sort_key .= $v[$key_key];
                    }
                    
$sort_flags SORT_STRING;
                }
                
$mapping[$k] = $sort_key;
            }
            
asort($mapping$sort_flags);
            
$sorted = array();
            foreach (
$mapping as $k => $v) {
                
$sorted[] = $array[$k];
            }
            return 
$sorted;
        }
    }
    return 
$array;
}

  • bh

    Thanks heaps for this. This is the only function of a dozen or so I’ve tried that actually works for me!

  • This worked perfectly for my needs. Thanks so much for posting it!

  • Pingback: Howto Show All Post on Wordpress MU Blogs | Bakawan Web Design()

  • Remi

    Work fine. Thank you very much ๐Ÿ˜€

    Remi

  • Miller

    didn’t work 4 me..:-(

  • What did you try to do? Do you have some code examples? Maybe I can help out…

  • Nefertina

    Greate solution. Helped me out.
    Might I suggest substituting :

    – asort($mapping, $sort_flags);

    with

    – uasort($mapping, ‘strcasecmp’);

    The latter makes the sorting case-insensitive.

  • Sergo

    Easy to customize:

    usort($array, function($a, $b){
    return $a[‘subkey’]<$b['sub_key'];
    });

  • Marcel

    Hello. I have to sort int values not strings, and for int values does not work. Some advise. thanks.

  • Hi Marcel,

    What you need to do is “prefix” the int values with zero’s, then sort them again.

    You can use http://php.net/manual/en/function.str-pad.php to make all the integers the same length (with STR_PAD_LEFT), and the SORT_STRING will work as expected.

  • Adam

    This worked great. Thanks ;]

  • thank you man, this worked perfectly for me! ๐Ÿ™‚ you rock!

  • Nick

    Hi – thanks – just the ticket! Added 3 things.
    (1) I needed to preserve the keys in my array

    $myArray=array(‘key1’=>array(‘x’=>x1,’y’=>y1,’z’=>z1),’key2’=>array(‘x’=>x2,’y’=>y2,’z’=>z2),etc…);

    So I replaced this line in the final loop $sorted[] = $array[$k]; with
    $sorted[$k]=$array[$k];

    (2) AND I needed the option of a reversed sort so included an $asc boolean as an optional input. If true then asort() else arsort()

    (3) Finally I needed it to work on numeric fields which were sub keys so allowed sort_flags to be a parameter input instead of SORT_STRING with potential input SORT_NUMERIC.

    But you saved me lots of time so thank you!

  • gatimu james

    It doesnt work with dates. (sorting a multidimension array using date column)

  • What is the format of the date?

  • Shoaib

    Great script..works like a charm. thanks for that ๐Ÿ™‚

  • jon

    Simply str_replace($date,”,’-‘) before order

  • jon

    Sorting without ‘-‘ will work on date ๐Ÿ˜›

  • Thanks for function, what my need is as below,

    Array
    [0](
    [list_price] = $2.85
    [desc] = QUAKER OATMEAL COOKIE 6 PACKS – APPLE AND CINNAMON FLAVOURED
    )
    [1](
    [list_price] = $5.85
    [desc] = QUAKER OATMEAL COOKIE
    )

    so now i want to sort this array based on how many words in “desc” field. i will least number of words in first.

    possible with this function ?

  • @jigs You can use a custom sort function instead of the asort function.

    An easy solution is to loop over the array, add a key ‘wordcount’ with an integer, pass it to the function, loop again and remove the key.

  • Thanks, you mean,

    $i = 0;
    foreach ($Array as $a) {
    $cnt = str_word_count($a[‘desc’]);
    $Array[$i][‘cnt’] = $cnt;
    $i++;
    }

    $Array = msort($Array, array(‘cnt’));

  • I tried above code and I guess it takes ‘cnt’ field as text field and return the array with following order,
    1
    11
    13
    15
    2
    21
    22
    3
    4
    5
    6

  • @jigs You need to added SORT_NUMERIC as the last parameter.

  • Tried that but not working, I guess this function already has this comment “// @TODO This should be fixed, now it will be sorted as string”

    Anyway, to avoid this i have passed string instead of array and it works as long as I have sort with one parameter.

    $Array = msort($Array, โ€˜cntโ€™); //works
    $Array = msort($Array, array(โ€˜cntโ€™)); // does not works

  • Coudenysj,

    I tried this as well,

    $Array = msort($Array, โ€˜cntโ€™, SORT_NUMERIC); //works
    $Array = msort($Array, array(โ€˜cntโ€™), SORT_NUMERIC); // does not works

    my site is http://compare.oyetweet.com and search string is “apple”

  • Jade Whelan

    Is there any way to use this to only print some of the key=>value pairs? I have an array with 20 elements, I’m only echoing 3 of them to the web page and I need to order these 3 alphabetically. This is in a 3d array that has been exploded twice and it doesn’t seem to matter what I do I just can’t sort them!

    I want to sort the array $cell, but I am only using $cell[2], $cell[11], and $cell[12], is there any way to only sort these 3 after using the explode()?

  • Working fine.

  • Harshu

    Thank U very much , it’s really helpfull

  • Kayo Almeida

    Hi man, i’m brazilian… Thanks, your function help me so much ๐Ÿ˜‰

  • Ivan

    thx for sharing i made a tiny modification for natural sorting. Go to http://writecodeonline.com/php/ then copy and paste ur code with modification…

    function msort($array, $key, $sort_flags = SORT_NATURAL) {
    if (is_array($array) && count($array) > 0) {
    if (!empty($key)) {
    $mapping = array();
    foreach ($array as $k => $v) {
    $sort_key = ”;
    if (!is_array($key)) {
    $sort_key = $v[$key];
    } else {
    // @TODO This should be fixed, now it will be sorted as string
    foreach ($key as $key_key) {
    $sort_key .= $v[$key_key];
    }
    $sort_flags = SORT_REGULAR;
    }
    $mapping[$k] = $sort_key;
    }
    //asort($mapping, $sort_flags);
    natsort($mapping);
    $sorted = array();
    foreach ($mapping as $k => $v) {
    $sorted[] = $array[$k];
    }
    return $sorted;
    }
    }
    return $array;
    }

    $a = array(array(‘unit_number’=>’55’),
    array(‘unit_number’=>’a2’),
    array(‘unit_number’=>’1’),
    array(‘unit_number’=>’10’),
    array(‘unit_number’=>’100’),
    array(‘unit_number’=>’b10’),
    array(‘unit_number’=>’b100’),
    array(‘unit_number’=>’ab23’),
    array(‘unit_number’=>’ac12’),
    array(‘unit_number’=>’8’),
    array(‘unit_number’=>’a1’),
    array(‘unit_number’=>’ab1’),
    array(‘unit_number’=>’ab100’),
    array(‘unit_number’=>’a44’),
    array(‘unit_number’=>’a5’)
    );
    $sorted = msort($a, array(‘unit_number’),SORT_NATURAL);

    $t = print_r($sorted,true);
    echo “$t”;

  • Thank you so much for this function, it is incredible how much it simplifies sorting of a multidimensional array :o)

    I am very grateful for finding this!

  • Cybernoid

    THANK YOU!

    This is the ONLY one that actually works. I’ve tried a dozen or so USORT-functions, including one shown here


    usort($array, function($a, $b){
    return $a[‘subkey’]<$b['sub_key'];
    });

    All they return is "1". Very useful. ๐Ÿ™‚

    But your function WORKS. And it works perfectly for what I needed. Thank you again!

  • Radu

    THANKS!

    This works very well for me!

  • Muhittin

    Thanks a lot of….

  • sam

    Worked like a BOSS!

  • redsoxfan99

    coudenysj,

    this worked perfect for simple 2 dimensional arrays but what about some really difficult multidimensional array, for example I have a deep multidimensional array that I cannot get sorted as every example I can find only uses 2 dimensions. It seems like you have a pretty good handle on this, do u have any examples using deeper dimensions arrays? for example, my array is as such:
    array (1)
    [“car_dealer”] => array(3)
    {
    [“id”] => string(4) “name”,
    [“name] => string(11) “dealer name”,

    [Units] => array (1)
    {
    [“Unit”] => array (5)
    {
    [0] => array(40)
    {
    [“id”] => string(5) “53553”,
    [“type”] => string(4) “SUV”,
    [“year”] => string(4) “2010”,
    [“brand”] => string(4) “Ford”,
    etc……..

    So how can I drill down to search units by brand or type?????

    Thanks for all of your help in advanced!

    LD

  • Nyukuri

    Hi,
    thank you, it works fine, but how can I get a DESC sort ? Setting the flag does not help.

  • Pratik

    thanx for help

  • Afaq Pakistan

    Very Very Good Work. God bless you.

  • AndyM

    You can also change your sort order if you make the following modifications:

    Remove the SORT_STRING line to enable the use of SORT_NUMERIC as a parameter

    $sort_key .= $v[$key_key];
    }
    // $sort_flags = SORT_STRING;
    }
    $mapping[$k] = $sort_key;

    Change the function to include a new parameter – $order:

    function msort($array, $key, $sort_flags = SORT_REGULAR, $order = SORT_ASC) {

    Add the following code instead of

    asort($mapping, $sort_flags);

    use

    switch ($order) {
    case SORT_ASC:
    asort($mapping, $sort_flags);
    break;
    case SORT_DESC:
    arsort($mapping, $sort_flags);
    break;
    }

    Now you should not only be able to sort on numeric fields, but can change the sort order. Especially useful if you need the big numbers at the top of the list ๐Ÿ™‚

    Hope this helps someone.

    AM

  • Rasta Boom

    Wew Thanks You,

  • Sergio Fiallo

    nice solution and works great even with dates. i know it is old but works great