从多维嵌套数组中删除数组的一部分

Removing part of an array from a multidimensional nested array

我有一个像这样的四层嵌套数组:

$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' => []
                            ]
                        ]
                    ]
                ]
            ]
        ]
    ]
];

现在,我想根据一些规则删除一些数组元素。为简单起见,假设我们要删除 'id' 等于 one.one.twoone.two.one.

的数组元素

我正在研究 Whosebug 上提供的一些答案,并试图应用它们来解决我的问题,但没有做太多。最棘手的事情是取消数组中不是数组中最远点的部分。

我正在尝试这样做以记录我想在哪个级别删除数组,然后尝试使用这些数组索引取消设置它们。

$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' => []
                            ]
                        ]
                    ]
                ]
            ]
        ]
    ]
];

如果能帮助我更有效地解决此问题,我将不胜感激。谢谢。

这是一个递归函数,可以执行您想要的操作。它遍历数组,寻找其网站 ID 在要删除的 ID 列表中的子项并取消设置它们。请注意,由于您有额外的 top-level 数组,您需要在这些值上迭代函数。

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);

输出如您所愿,但太长无法在此处重现。参见 demo on 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);
    }
}

Updated demo