2

Sorting an multidimensional array by one of its key value (asc or desc) is asked too many times. but I've not found any solution for my problem where you have to sort a multidimensional array by one of its key value in predefined custom order.

take an array for example

$array[0] = array('id'=> '111', value=>'abc');
$array[1] = array('id'=> '100', value=>'abc');
$array[2] = array('id'=> '132', value=>'abc');
$array[3] = array('id'=> '222', value=>'abc');
$array[4] = array('id'=> '112', value=>'abc');
$array[5] = array('id'=> '200', value=>'abc');

and to sort this array by subkey 'id' by the order defined in array below:

$sort_order_id = array('112','111','132','100');

so the result would be

$array[0] = array('id'=> '112', value=>'abc');
$array[1] = array('id'=> '111', value=>'abc');
$array[2] = array('id'=> '132', value=>'abc');
$array[3] = array('id'=> '100', value=>'abc');
$array[4] = array('id'=> '222', value=>'abc');
$array[5] = array('id'=> '200', value=>'abc');

NOTE: if some of the id's are not available in $sort_order_id array, keep them in last (like in above example id 222 and 200 are not in $sort_order_id array, so it comes in last as per their previous order)

i tried a lot but unable to create algorithm for that... i hope some one will help me...

Dr. DS
  • 984
  • 1
  • 13
  • 31
  • why negative vote? pls explain... this question has never been asked before in any community – Dr. DS Feb 28 '14 at 12:23
  • 1
    see [Sorting a php array of arrays by custom order](http://stackoverflow.com/questions/11145393/sorting-a-php-array-of-arrays-by-custom-order) – Satish Sharma Feb 28 '14 at 12:26
  • @SatishSharma , above question is slight different since it doesn't deals if the subkey id is not in sortorder. read my question properly. – Dr. DS Feb 28 '14 at 12:30

4 Answers4

5

Take the standard code for sorting an array by id:

usort($data, function($x, $y) { return $x['id'] - $y['id']; });

What you want to do is sort not by the ids themselves, but rather by the "weight" of each id (which represents the arbitrary order you want the ids to be put in). So make an array where keys are ids and values are the weights and use it to convert ids to weights:

// array_flip turns 0 => 112, 1 => 111, ... into 122 => 0, 111 => 1, ...
$weights = array_flip(array('112','111','132','100'));

usort($data, function($x, $y) use($weights) {
    return $weights[$x['id']] - $weights[$y['id']];
});

Update: Since you also want to handle ids that do not have a specified weight you will have to further expand the above:

usort($data, function($x, $y) use($weights) {
    if (!isset($weights[$x['id']], $weights[$y['id']])) {
        // none of the ids have weight, so sort by bare id
        return $x['id'] - $y['id'];
    }
    else if (!isset($weights[$x['id']])) { 
        // x does not have weight, put it last
        return 1;
    }
    else if (!isset($weights[$y['id']])) {
        // y does not have weight, put it last
        return -1;
    }

    // both have weights, use them
    return $weights[$x['id']] - $weights[$y['id']];
});
Jon
  • 428,835
  • 81
  • 738
  • 806
  • accepted as answer, since its faster than others. thank you... replaced line if (!isset($weights[$x['id']], $weights[$y['id']])) { with if (!isset($weights[$x['id']]) && !isset($weights[$y['id']])) { – Dr. DS Feb 28 '14 at 12:22
1

@Jon's answer can be much more concise with the spaceship operator and null coalescing.

The id ordering array becomes a convenience lookup once it is flipped. If a given row's id value is not found in the ordering array, then it is pushed "later" in the output array.

Code: (Demo)

$array = [
    ['id' => '111', 'value' => 'abc'],
    ['id' => '100', 'value' => 'abc'],
    ['id' => '132', 'value' => 'abc'],
    ['id' => '222', 'value' => 'abc'],
    ['id' => '112', 'value' => 'abc'],
    ['id' => '200', 'value' => 'abc']
];

$idPriorities = array_flip(['112', '111', '132', '100']);
$idOutlier = count($idPriorities);

usort(
    $array,
    function($a, $b) use ($idPriorities, $idOutlier) {
        return ($idPriorities[$a['id']] ?? $idOutlier) <=> ($idPriorities[$b['id']] ?? $idOutlier);
    }
);

var_export($array);

or with PHP7.4+ (Demo)

usort(
    $array,
    fn($a, $b) => ($idPriorities[$a['id']] ?? $idOutlier) <=> ($idPriorities[$b['id']] ?? $idOutlier)
);
mickmackusa
  • 43,625
  • 12
  • 83
  • 136
0

You need usort(), which allows you to sort by bespoke requirements. In your case, you need to sort by the ID's position within your sort_order_id array.

$array = array(
    array('id'=> '111', value=>'abc'),
    array('id'=> '100', value=>'abc'),
    array('id'=> '132', value=>'abc'),
    array('id'=> '112', value=>'abc')
);
$sort_order_id = array('112','111','132','100');
usort($array, function($a, $b) {
    global $sort_order_id;
    return array_search($a['id'], $sort_order_id) - array_search($b['id'], $sort_order_id);
});
Mitya
  • 33,629
  • 9
  • 60
  • 107
  • 1
    Flipping the sort order array before sorting will allow you to replace the `O(n)` `array_search` with an `O(1)` array lookup, potentially making the sort extremely faster. – Jon Feb 28 '14 at 12:09
0

try this one

echo "<pre>";

$array[0] = array('id'=> '111', 'value'=>'abc');
$array[1] = array('id'=> '100', 'value'=>'abc');
$array[2] = array('id'=> '132', 'value'=>'abc');
$array[3] = array('id'=> '222', 'value'=>'abc');
$array[4] = array('id'=> '112', 'value'=>'abc');
$array[5] = array('id'=> '200', 'value'=>'abc');

$arr_temp = $array;
$array = array();
$sort_order_id = array('112','111','132','100');
foreach($sort_order_id as $order_id)
{
    foreach($arr_temp as $key=>$arr)
    {
        if($arr['id'] == $order_id)
        {
            $array[] = $arr;
            unset($arr_temp[$key]);
        }
    }
}

foreach($arr_temp as $key=>$arr)
{
        $array[] = $arr;
        unset($arr_temp[$key]);
}

print_r($array);

OUTPUT :

Array
(
    [0] => Array
        (
            [id] => 112
            [value] => abc
        )

    [1] => Array
        (
            [id] => 111
            [value] => abc
        )

    [2] => Array
        (
            [id] => 132
            [value] => abc
        )

    [3] => Array
        (
            [id] => 100
            [value] => abc
        )

    [4] => Array
        (
            [id] => 222
            [value] => abc
        )

    [5] => Array
        (
            [id] => 200
            [value] => abc
        )

)
Satish Sharma
  • 9,547
  • 6
  • 29
  • 51
  • 1
    This is going to be as slow as possible. You are effectively doing a [selection sort](http://en.wikipedia.org/wiki/Selection_sort) with a needlessly high constant coefficient. – Jon Feb 28 '14 at 12:18