1

i do not unstand the way to sort a multi-dimensional array in php. I have a structure like this:

Array (
   [0] (
      ['vname'] => "Bernt"
      ['nname'] => "Mayer"
      ['kl_name'] => "ZR4"
   )
   [1] (
      ['vname'] => "Albert"
      ['nname'] => "Mayer"
      ['kl_name'] => "TR4"
   )
)

My goal is now to first sort by kl_name, and then by nname and then vname. Most important is kl_name. First i tought i should make objects and store them in the array, but i think sorting them is even more complex. php.net has a nice article about array_multisort, but year, i dont understand it :/

Jannik
  • 95
  • 1
  • 8
  • 1
    If you're getting those values from a database, you might as well sort it directly by modifying your query – HamZa Jul 31 '14 at 17:02
  • 1
    Also extremely related if not a duplicate: [Reference: all basic ways to sort arrays and data in PHP](http://stackoverflow.com/questions/17364127/reference-all-basic-ways-to-sort-arrays-and-data-in-php) – HamZa Jul 31 '14 at 17:05

3 Answers3

4

You'll want to sort the array with a user defined function (with usort).

You can then manually specify how you'd like the items to be ordered using the kl_name, nname and vname attributes.

Something like this:

usort($arr, function($a, $b) {
    if ($a['kl_name'] !== $b['kl_name']) {
        return strcmp($a['kl_name'], $b['kl_name']);
    } else if ($a['nname'] !== $b['nname']) {
        return strcmp($a['nname'], $b['nname']);
    } else {
        return strcmp($a['vname'], $b['vname']);
    }
});

The function will first attempt to sort by kl_name, then by nname and finally by vname if all previous values are equal.

Overv
  • 8,433
  • 2
  • 40
  • 70
  • Year! Thats works great in my case. never saw usort bevor (shame on me) but it looks great. Has someone an idea about performance of usort and array_multisort? – Jannik Jul 31 '14 at 17:32
  • 2
    That's an interesting question. I'd suspect `array_multisort` to be quicker as the sorting takes place in PHP's C code rather than the userland callback function, but this speedup might be offset by the need to transpose rows/columns at the start. If you want to know then you'll have to measure it! – simpleigh Jul 31 '14 at 17:34
3

You can use array_multisort for this.

// Obtain a list of columns
foreach ($data as $key => $row) {
    $kl_name[$key] = $row['kl_name'];
    $nname[$key]   = $row['nname'];
    $vname[$key]   = $row['vname'];
}

// Sort
array_multisort($kl_name, SORT_ASC, $nname, SORT_ASC, $vname, SORT_ASC, $data);

See http://php.net/array_multisort for more details.

simpleigh
  • 2,854
  • 18
  • 19
  • If you have PHP >= 5.5, then [`array_column`](http://php.net/array_column) could be useful here instead of the `foreach`. Example: https://eval.in/173777 – gen_Eric Jul 31 '14 at 17:13
  • I've never understood `array_multisort`. So, you need to create an array for each field you want, then sort by all of them? – gen_Eric Jul 31 '14 at 17:21
  • 1
    No, I don't really understand it either - I cribbed off the documentation to produce the answer. It sorts several arrays at once, making sure that swaps made on one array are made to all of them. The annoying first step is required to transpose the row-oriented dataset into a column-oriented form. – simpleigh Jul 31 '14 at 17:32
1

I've never understood array_multisort either. I just use usort for things like this. What you do is, you compare the 1st field, and if it's the same, then you compare the 2nd... and so on.

So, it would look something like this (assuming $data is your array).

usort($data, function($a, $b){
    $sort_kl_name = strnatcasecmp($a['kl_name'], $b['kl_name']);
    if($sort_kl_name === 0){
        $sort_nname = strnatcasecmp($a['nname'], $b['nname']);
        if($sort_kl_name === 0){
            return strnatcasecmp($a['vname'], $b['vname']);
        }
        return $sort_nname;
    }
    return $sort_kl_name;
});

This looks a little messy, with lots of ifs. Let's simplify that with a foreach.

$sortLevels = array('kl_name', 'nname', 'vname');

usort($data, function($a, $b) use($sortLevels){
    foreach($sortLevels as $field){
        $sort = strnatcasecmp($a[$field], $a[$field]);
        if($sort !== 0){
            break;
        }
    }
    return $sort;
});

DEMO: https://eval.in/173774

gen_Eric
  • 223,194
  • 41
  • 299
  • 337