PHP - Sort multi-dimensional array by another array

There is no built-in function for this in PHP and i am unable to think of any custom function, which would do this using usort. But array_map is simple enough, imo, so why not use it instead?

$sorted = array_map(function($v) use ($data) {
    return $data[$v - 1];
}, $order);

In your example the ids in the $data array are are numbered consecutively and starting at 1. The code I give below assumes this is always the case. If this is not the case, the code does not work.

$result = array();
$index = 0;
foreach ($order as $position) {
    $result[$index] = $data[$position - 1];
    $index++;
}

At http://codepad.org/YC8w0yHh you can see that it works for your example data.

EDIT

If the assumption mentioned above does not hold, the following code will achieve the same result:

<?php

$data = array(
    array('id' => 1, 'title' => 'whatever'),
    array('id' => 2, 'title' => 'whatever'),
    array('id' => 3, 'title' => 'whatever')
);

$order = array(2,3,1);
$order = array_flip($order);

function cmp($a, $b)
{
    global $order;

    $posA = $order[$a['id']];
    $posB = $order[$b['id']];

    if ($posA == $posB) {
        return 0;
    }
    return ($posA < $posB) ? -1 : 1;
}

usort($data, 'cmp');

var_dump($data);

See http://codepad.org/Q7EcTSfs for proof.

By calling array_flip() on the $order array it can be used for position lookup. This is like a hashtable lookup, which is linear in time, or O(n). You cannot do better.


For those of you who want to sort data based on an array with actual IDs, rather than based on an array with indexes like in the accepted answer - you can use the following simple comparison function for the usort:

usort($data, function($a, $b) use ($order) {
    $posA = array_search($a['id'], $order);
    $posB = array_search($b['id'], $order);
    return $posA - $posB;
});

So the following example will work fine and you won't get the Undefined offset notices and an array with null values:

$order = [20, 30, 10];

$data = [
    ['id' => 10, 'title' => 'Title 1'],
    ['id' => 20, 'title' => 'Title 2'],
    ['id' => 30, 'title' => 'Title 3']
];

usort($data, function($a, $b) use ($order) {
    $posA = array_search($a['id'], $order);
    $posB = array_search($b['id'], $order);
    return $posA - $posB;
});

echo '<pre>', var_dump($data), '</pre>';

Output:

array(3) {
  [0]=>
  array(2) {
    ["id"]=>
    int(20)
    ["title"]=>
    string(7) "Title 2"
  }
  [1]=>
  array(2) {
    ["id"]=>
    int(30)
    ["title"]=>
    string(7) "Title 3"
  }
  [2]=>
  array(2) {
    ["id"]=>
    int(10)
    ["title"]=>
    string(7) "Title 1"
  }
}

This would be how I would do. I would use a custom usort function (arr_sort) in conjunction with the $data array.

<?php
$order = array(2,3,1);
$data = array(
    array('id' => 1, 'title' => 'whatever'),
    array('id' => 2, 'title' => 'whatever'),
    array('id' => 3, 'title' => 'whatever')
);
function arr_sort($a,$b){
  global $order;
  foreach ($order as $key => $value) {
    if ($value==$a['id']) {
      return 0;
      break;
    }
    if ($value==$b['id']) {
      return 1;
      break;
    }
  }
}
usort($data,'arr_sort');
echo "<pre>";
print_r($data);
echo "<pre>";