V
V
Vadim2016-09-02 15:02:32
PHP
Vadim, 2016-09-02 15:02:32

How to sort an array with dependencies?

There is an array inside which elements can depend on each other. For example, like this:

$items = [
  'item_a' => ['require' => ['item_b']], // этот элемент зависит от 'item_b'
  'item_b' => ['require' => []], // этот элемент не зависит от других
  'item_c' => ['require' => ['item_a']],
  'item_d' => ['require' => ['item_a', 'item_e']], // этот элемент зависит сразу от двух других - 'item_a' и 'item_b'
  'item_e' => ['require' => ['item_a']],
];

And you need to sort it so that the dependent elements always go after the elements on which they depend. In my example, the order of keys as a result of sorting will be:
'item_b', 'item_a', 'item_c', 'item_e', 'item_d'
Actually, the question is: how to perform such sorting? Ideally, if there are some ready-made libraries for such sorting.
PS. Ash stump, that there may be cyclic dependencies and this must also be taken into account somehow.

Answer the question

In order to leave comments, you need to log in

1 answer(s)
C
Cat Anton, 2016-09-02
@vshemarov

How do you propose to sort this?

$items = [
    'item_a' => ['require' => ['item_b']],
    'item_b' => ['require' => ['item_с']],
    'item_c' => ['require' => ['item_a']],
];

If there are no cycles and interdependencies, then like this:
foreach (array_keys($items) as $key) {
    $items[$key]['name'] = $key;
}

uasort($items, function($a, $b) {
    return (in_array($b['name'], $a['require']) || empty($b['require']));
});

https://ideone.com/ClHFqW
To search for cyclic dependencies, I got the following function:
/**
 * @param array $items
 * @return bool
 */
function search_cycle($items)
{
    $search = function(array $path) use ($items, &$search)
    {
        $count = count($path);
        $first = $path[0];
        $last  = $path[$count - 1];
        if ($count > 1 && in_array($last, array_slice($path, 0, $count - 1))) {
            return true;
        }
        
        foreach ($items[$last]['require'] as $key) {
            if ($search(array_merge($path, [$key]))) {
                return true;
            }
        }
        
        return false;
    };
    
    foreach ($items as $key => $data) {
        if ($search([$key])) {
            return true;
        }
    }
    
    return false;
}

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question