Удаление части массива из многомерного вложенного массива - PullRequest
0 голосов
/ 29 мая 2019

У меня есть четыре уровня вложенного массива, как это:

$array = [
    [
        'website' => [
            'id' => 'one'
        ],
        'children' => [
            [
                'website' => [
                    'id' => 'one.one'
                ],
                'children' => [
                    [
                        'website' => [
                            'id' => 'one.one.one'
                        ],
                        'children' => [
                            [
                                'website' => [
                                    'id' => 'one.one.one.one'
                                ],
                                'children' => []
                            ],
                            [
                                'website' => [
                                    'id' => 'one.one.one.two'
                                ],
                                'children' => []
                            ]
                        ]
                    ],
                    [
                        'website' => [
                            'id' => 'one.one.two'
                        ],
                        'children' => [
                            [
                                'website' => [
                                    'id' => 'one.one.two.one'
                                ],
                                'children' => []
                            ],
                            [
                                'website' => [
                                    'id' => 'one.one.two.two'
                                ],
                                'children' => []
                            ]
                        ]
                    ]
                ]
            ],
            [
                'website' => [
                    'id' => 'one.two'
                ],
                'children' => [
                    [
                        'website' => [
                            'id' => 'one.two.one'
                        ],
                        'children' => [
                            [
                                'website' => [
                                    'id' => 'one.two.one.one'
                                ],
                                'children' => []
                            ],
                            [
                                'website' => [
                                    'id' => 'one.two.one.two'
                                ],
                                'children' => []
                            ]
                        ]
                    ],
                    [
                        'website' => [
                            'id' => 'one.two.two'
                        ],
                        'children' => [
                            [
                                'website' => [
                                    'id' => 'one.two.two.one'
                                ],
                                'children' => []
                            ],
                            [
                                'website' => [
                                    'id' => 'one.two.two.two'
                                ],
                                'children' => []
                            ]
                        ]
                    ]
                ]
            ]
        ]
    ]
];

Теперь я хотел бы удалить некоторые элементы массива на основе некоторых правил. Допустим, для простоты мы хотели бы удалить элементы массива с идентификатором, равным one.one.two или one.two.one.

Я изучал некоторые ответы, представленные на stackoverflow, и пытался применить их для решения моей проблемы, но ничего не получалось. Самое сложное - это сбросить часть массива, где это не самая дальняя точка массива.

Я пытался это отметить, на каком уровне я хочу удалить массивы, а затем пытался сбросить их, используя индекс этих массивов.

$pointer = [];
foreach($array as $key => $level1) {
    $level1pointer = $key;
    $pointer[] = markToDelete($level1, $level1pointer);
    foreach($level1['children'] as $key => $level2) {
        $level2pointer = $key;
        $pointer[] = markToDelete($level2, $level1pointer, $level2pointer);
        foreach($level2['children'] as $key => $level3) {
            $level3pointer = $key;
            $pointer[] = markToDelete($level3, $level1pointer, $level2pointer, $level3pointer);
            foreach($level3['children'] as $key => $level4) {
                $level4pointer = $key;
                $pointer[] = markToDelete($level4, $level1pointer, $level2pointer, $level3pointer, $level4pointer);
            }
        }
    }
}

function markToDelete($array, $level1 = null, $level2 = null, $level3 = null, $level4 = null) {
    $exclusionList = [
        'one.one.two',
        'one.two.one'
    ];

    if (!empty($array['website']) && in_array($array['website']['id'], $exclusionList)) {
        print_r('marking for deletion: '. $array['website']['id'] . PHP_EOL);
        return [
            'id' => $array['website']['id'],
            'level1' => $level1,
            'level2' => $level2,
            'level3' => $level3,
            'level4' => $level4
        ];
    }
    return [];
}

Я также пытался использовать итератор так:

$it = new \RecursiveIteratorIterator(new \RecursiveArrayIterator($array), \RecursiveIteratorIterator::LEAVES_ONLY);
$newArray = [];
foreach($it as $key => $value) {
    $exclusionList = [
        'one.one.two',
        'one.two.one'
    ];
    if(!in_array($value, $exclusionList)) {
        print_r(sprintf('value: %s is ready to be deleted', $value).PHP_EOL);
        $newArray[] = $value;
    }
}

но мне нужен способ сбросить массив при циклическом переборе итератора.

Я бы хотел получить такой вывод:

$array = [
    [
        'website' => [
            'id' => 'one'
        ],
        'children' => [
            [
                'website' => [
                    'id' => 'one.one'
                ],
                'children' => [
                    [
                        'website' => [
                            'id' => 'one.one.one'
                        ],
                        'children' => [
                            [
                                'website' => [
                                    'id' => 'one.one.one.one'
                                ],
                                'children' => []
                            ],
                            [
                                'website' => [
                                    'id' => 'one.one.one.two'
                                ],
                                'children' => []
                            ]
                        ]
                    ],
                ]
            ],
            [
                'website' => [
                    'id' => 'one.two'
                ],
                'children' => [
                    [
                        'website' => [
                            'id' => 'one.two.two'
                        ],
                        'children' => [
                            [
                                'website' => [
                                    'id' => 'one.two.two.one'
                                ],
                                'children' => []
                            ],
                            [
                                'website' => [
                                    'id' => 'one.two.two.two'
                                ],
                                'children' => []
                            ]
                        ]
                    ]
                ]
            ]
        ]
    ]
];

Я бы очень признателен за помощь в том, как решить эту проблему более эффективным способом. Спасибо.

1 Ответ

1 голос
/ 29 мая 2019

Вот рекурсивная функция, которая будет делать то, что вы хотите. Он пересекает массив, ищет детей, чей идентификатор сайта находится в списке идентификаторов, которые нужно удалить, и сбрасывает их. Обратите внимание, что из-за вашего дополнительного массива верхнего уровня вам нужно выполнить итерации по этим значениям.

function delete_entries(&$array, $ids_to_delete) {
    foreach ($array['children'] as $index => &$child) {
        if (in_array($child['website']['id'], $ids_to_delete)) {
            unset($array['children'][$index]);
        }
        delete_entries($child, $ids_to_delete);
    }
}

foreach ($array as &$arr) {
    delete_entries($arr, array('one.one.two', 'one.two.one'));
}

var_export($array);

Результат такой, какой вы хотите, но он слишком длинный для воспроизведения здесь. См. Демоверсию на 3v4l.org

Обновление

Приведенный выше код не удалит записи на верхнем уровне, поскольку структура массива отличается от нижних уровней в массиве. С этим можно справиться во внешнем цикле foreach:

$excluded = array('two', 'one.one.two', 'one.two.one');
foreach ($array as $key => &$arr) {
    if (in_array($arr['website']['id'], $excluded)) {
        unset($array[$key]);
    }
    else {
        delete_entries($arr, $excluded);
    }
}

Обновленная демоверсия

...