2
\$\begingroup\$

I have an array like this:

$array = [
    ['id' => 1, 'content' => 'value 1'],
    ['id' => 2, 'content' => 'value 2'],
    ['id' => 3, 'content' => 'value 3'],
];

I have another array containing the actual order of ids in which $array should be sorted :

$order = [3, 1, 2];

Which means, the result should be this:

$sorted = [
    0 => ['id' => 3, 'content' => 'value 3'],
    1 => ['id' => 1, 'content' => 'value 1'],
    2 => ['id' => 2, 'content' => 'value 2'],
];

The final requirement is, that the array should be printed using foreach:

foreach($sorted as $value) {
    print $value['content'];
}

I came up with this solution:

function orderArray(array $array, array $order) {
    $sorted = [];

    foreach($array as $value) {
        $id = $value['id'];
        $index = array_search($id, $order);
        $sorted[$index] = $value;
    }

    ksort($sorted);

    return $sorted;
}

This works fine. However, I wonder whether this is efficent, especially as I have to call ksort to make the array be printed correctly by foreach.

Can this be optimized? Maybe there's also a more elegant solution to the problem.


It's a given, that all ids and only those are in $order - so no need to test for that.

\$\endgroup\$
7
  • \$\begingroup\$ Do the IDs in $array always correspond to the numbers in the $order array? \$\endgroup\$
    – Dave
    Aug 31, 2018 at 10:23
  • \$\begingroup\$ @Dave Yes, they do. $order contains all ids from $array and no other elements. I tried to say that in the footnote - hopefully it's clearer now. \$\endgroup\$
    – lampshade
    Aug 31, 2018 at 10:25
  • \$\begingroup\$ In your $array, the ids are in order. Is this always the case? Or is the input array not ordered? \$\endgroup\$
    – maxb
    Aug 31, 2018 at 11:25
  • \$\begingroup\$ @maxb it is safe to assume that the order (or the consecuteveness) are just an accident. \$\endgroup\$ Aug 31, 2018 at 11:27
  • \$\begingroup\$ @maxb In this particular use case it's always ordered from 1 to n in $array. \$\endgroup\$
    – lampshade
    Aug 31, 2018 at 11:45

5 Answers 5

1
\$\begingroup\$

I think this would be the cleanest solution. Similar to @YourCommonSense answer, but slightly inverted as ordered index is already there - only values missing.

$sorted = array_flip($order);
foreach ($array as $value) {
    $id = $value['id'];
    $sorted[$id] = $value;
}
\$\endgroup\$
0
1
\$\begingroup\$

Since your ordering array contains all of the ids in your data array, you can prepare the two arrays to have relatable keys then just call array_replace(). This means a fully "functional style" instead of classic foreach loops (if you like them). The foreach() loops often outperform function calls due to having less overhead, but with my snippet you won't need to declare the output variable before returning it.

  • array_flip() provides relatable keys for $order.
  • array_column() provides relatable keys for $array without mutating the values.
  • array_replace() simply overwrites the $order values with the $array data (all sorted now).
  • array_values() re-indexes the array (removes the temporary, relatable keys).

Code:

function orderArray(array $array, array $order): array {
    return array_values(
               array_replace(
                   array_flip($order),
                   array_column($array, null, 'id')
               )
           );
}

I find this declarative snippet to be more elegant and easier to read since it doesn't have nested square braces to try to decipher nor any newly declared variables. Again, performance is not the benefit here, but with your sample data being so small, it is unlikely that a user will notice and difference between any of the snippets on this page.

\$\endgroup\$
0
\$\begingroup\$

I would say that array_search() is also a concern.

So I would create an index array to make a correspondence between the id and the position and then just create a new array, like this

$sorted = [];
$index = array_flip(array_column($array, 'id'));
foreach ($order as $id) {
    $sorted[] = $array[$index[$id]];
}
\$\endgroup\$
0
\$\begingroup\$

If (and only if) the $array that you start with is always ordered from 1 to n (as you said in the comments), then you don't have to do any sorting on your part. It's simply a matter of accessing your array. I came up with the following solution:

$array = [
    ['id' => 1, 'content' => 'value 1'],
    ['id' => 2, 'content' => 'value 2'],
    ['id' => 3, 'content' => 'value 3'],
    ['id' => 4, 'content' => 'value 4'],
];

$order = [3, 1, 2, 4];
$sorted = [];

$i = 0;
foreach ($array as $value) {
    $sorted[$i] = $array[$order[$i]-1];
    $i++;
}

However, I'm afraid that I've misunderstood your question if this answers it. The code produces identical output compared to the function in the question, provided that the $array is sorted. Otherwise it breaks horribly.

If this doesn't fit your use case, write a comment and I'll try to see what's missing.

\$\endgroup\$
1
  • 1
    \$\begingroup\$ you can already have $i from foreach \$\endgroup\$ Aug 31, 2018 at 15:14
-1
\$\begingroup\$

Assuming you aren't going to use the sorted array you are creating for something else just use the $order array to echo the values directly.

$array = [
    ['id' => 1, 'content' => 'value 1'],
    ['id' => 2, 'content' => 'value 2'],
    ['id' => 3, 'content' => 'value 3'],
];

$order = [3, 1, 2];

foreach ($order as $point) {
   echo $array[$point - 1]['content'] . PHP_EOL;
}
\$\endgroup\$
2
  • \$\begingroup\$ This is not a code an experienced programmer would consider acceptable. Ids are not guaranteed to be in order. Such an assumption would lead to innumerable problems. \$\endgroup\$ Aug 31, 2018 at 11:24
  • \$\begingroup\$ Won't argue with you at all but based on his parameters and his comments it will work as he desires. Fragile as can be and not the best solution at all. \$\endgroup\$
    – Dave
    Aug 31, 2018 at 11:39

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.