1

I want to sort a tree hierarchy array with structure as below based on a key (in this example: timestamp).

$topics = array(
  array('name' => 'n1', 'timestamp' => 5000, 'children' => array()),
  array('name' => 'n2', 'timestamp' => 4000, 'children' => array(
    array('name' => 'n3', 'timestamp' => 6000, 'children' => array()),
    array('name' => 'n4', 'timestamp' => 2000, 'children' => array(
      array('name' => 'n5', 'timestamp' => 4000, 'children' => array()),
      array('name' => 'n6', 'timestamp' => 3000, 'children' => array())
    )), 
  )),
  array('name' => 'n7', 'timestamp' => 1000, 'children' => array())
);

My sort function:

function sequenceSort(&$a, &$b) {
  if (!empty($a['children'])) {
    usort($a['children'], 'sequenceSort');
  }
  if ($a['timestamp'] == $b['timestamp']) {
    return 0;
  }
  return $a['timestamp'] < $b['timestamp'] ? -1 : 1;
}

usort($topics, 'sequenceSort');
print_a($topics);

At some levels it produces the correct output, at another it doesn't, e.g:

1000 ✔
4000 ✔
   6000 ✘
   2000 ✘
      4000 ✘
      3000 ✘
5000 ✔

What's wrong with this?

5
  • You cant modify an array inside usort Commented Aug 9, 2018 at 7:22
  • @kaspars I use pass by references to achieve that. Commented Aug 9, 2018 at 7:25
  • Note that you should order your input array differently to see if it really sorts. The second level now also fails but you don't see that because you have already sorted it correctly. Commented Aug 9, 2018 at 7:28
  • 1
    @jeroen I've edited the question with more random input order. Commented Aug 9, 2018 at 7:32
  • @emix It should be <=> (single =), right? Commented Aug 9, 2018 at 7:38

1 Answer 1

1

This doesn't work because usort() doesn't pass the array elements by reference, even though you've declared the parameters with &. I added:

$a['touched'] = true;

to the comparison function, and when I printed the result these keys were nowhere to be found.

However, even if it worked, this seems like a very poor way to do it, as it will sort the children multiple times -- every time the parent is being compared with another parent, it will have to re-sort the children, and grandchildren, etc.

It would be better to write a recursive function that sorts one level, then iterates over the children, and so on.

function sortRecurse(&$array) {
    usort($array, function($a, $b) {
        return $a['timestamp'] - $b['timestamp'];
    });
    foreach ($array as &$subarray) {
        sortRecurse($subarray['children']);
    }
}

DEMO

Sign up to request clarification or add additional context in comments.

6 Comments

That's what I thought, but this does not sort correctly either.
@JustinusHermawan It doesn't work because the elements aren't passed by reference to the comparison function. So Kaspar's comment above is correct.
@barmar Yes, it works. So, the mistake here is usort() function does not retrieve params by reference, right?
It gets its own parameter by reference, but it doesn't pass parameters to the comparison function by reference.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.