Сглаживание многоуровневого массива JSON в PHP

Доброе утро, учитывая приведенную ниже структуру данных (в формате JSON для удобства чтения)

[
{
    "parent": "root",
    "active": "1",
    "label": "Index",
    "route": "/",
    "children": [
        {
            "parent": "/",
            "active": "1",
            "label": "Products",
            "route": "/products",
            "children": [
                {
                    "parent": "/products",
                    "active": "0",
                    "label": "Test",
                    "route": "/test"
                }
            ]
        }
    ]
    },
    {
        "parent": "root",
        "active": "1",
        "label": "404",
        "route": "/404"
    },
    {
        "parent": "root",
        "active": "1",
        "label": "Login",
        "route": "/login"
    }
]

У меня возникли серьезные проблемы с возвратом из функции следующей структуры:

[
{
    "parent": "root",
    "active": "1",
    "label": "Index",
    "route": "/"
},
{
    "parent": "/products",
    "active": "0",
    "label": "Test",
    "route": "/test"
},
{
    "parent": "/",
    "active": "1",
    "label": "Products",
    "route": "/products"
},
{
    "parent": "root",
    "active": "1",
    "label": "404",
    "route": "/404"
},
{
    "parent": "root",
    "active": "1",
    "label": "Login",
    "route": "/login"
}
]

По сути, я хочу выполнить рекурсию по всем дочерним элементам и заполнить новый массив каждым родителем и дочерним элементом во вложенных массивах, я пробовал array_merge, RecursiveIteratorIterator, itterator_to_array, array_map, но он всегда не застревает в рекурсии. Мне удалось это сделать, когда дети были только на один уровень в глубину, а два или более просто ломаются.

Пожалуйста помоги!


person user3169851    schedule 08.02.2015    source источник
comment
Пробовали ли вы stackoverflow.com/a/1320259/2529486   -  person VeeeneX    schedule 08.02.2015
comment
VeeeneX: Да, они не дают мне желаемых результатов, они возвращают только родителей, а не детей.   -  person user3169851    schedule 08.02.2015


Ответы (2)


Не очень сложно:

function flatten(array $array) {
    $branch = [];

    foreach ($array as $item) {
        $children = [];
        if (isset($item['children']) && is_array($item['children'])) {
            $children = flatten($item['children']);
            unset($item['children']);
        }
        $branch = array_merge($branch, [$item], $children);
    }

    return $branch;
}
person deceze♦    schedule 08.02.2015

Очень просто

function flatten($items, &$r) {
    foreach($items as $item) {
        $c = isset($item->children) ? $item->children : null;
        unset($item->children);
        $r []= $item;
        if($c)
            flatten($c, $r);
    }
}

flatten(json_decode($json), $r);
print_r($r);

Это накапливает результаты в одном единственном буфере, переданном по ссылке. Это намного эффективнее, чем создание нового массива на каждой итерации, что в основном является разновидностью Shlemiel. алгоритм художника.

Если вы предпочитаете функциональный подход, вы можете использовать генераторы:

function flatten($items) {
    foreach($items as $item) {
        $c = isset($item->children) ? $item->children : [];
        unset($item->children);
        yield $item;
        foreach(flatten($c) as $child)
            yield $child;
    }
}

foreach(flatten(json_decode($json)) as $item)
    print_r($item);
person georg    schedule 08.02.2015
comment
Я, конечно, соглашусь, что мой алгоритм гораздо более расточительный. Я просто предпочитаю функциональный подход и нотацию передаче результата в качестве аргумента по ссылке. Это должно быть проверенным узким местом для меня, чтобы переписать это по-вашему. +1 тем не менее :) - person deceze♦; 08.02.2015
comment
Легко ли обратимы эти функции? Например, взять их структуру вывода и превратить их обратно в мой оригинальный json? - person user3169851; 09.02.2015
comment
@ user3169851: да, перебрать плоский результат и добавить текущий элемент в $tree[$item->parent]->children. - person georg; 09.02.2015