如何移除不同数组维度间的重复数据?基于自关联导航菜单场景
解决自关联导航菜单多维数组的去重问题
看起来你是在处理树形结构的导航菜单数据,用Eloquent预加载子节点后得到了嵌套数组,现在需要清理掉不同层级里的重复项对吧?我来给你几个可行的方案:
一、从数据库查询层面避免重复(推荐)
如果重复数据是因为查询逻辑导致的(比如某些子节点被意外多次加载),可以先从根源上解决:
确保关联定义正确
你的childs关联应该是这样的(在App\Menu模型里):public function childs() { return $this->hasMany(Menu::class, 'id_parent', 'id')->distinct(); }这里加上
distinct()可以确保每个子节点只被查询一次,避免因为关联关系导致的重复加载。查询时添加唯一约束
如果你的重复是指同一个id的菜单出现在不同层级(比如某个菜单既是顶级又是子菜单),可以在查询时过滤掉重复的id:$menus = App\Menu::with(['childs' => function ($query) { $query->distinct(); }])->get()->unique('id');unique('id')会在顶级集合里去重,但子节点里的重复还需要递归处理,所以接下来看数组层面的方案。
二、数组层面递归去重(处理嵌套结构的重复)
如果数据库层面没法完全避免,或者你需要基于url等其他字段去重,可以写一个递归函数来遍历整个嵌套数组,记录已经出现过的标识(比如id或url),然后移除重复项:
示例代码(基于id去重)
function removeDuplicateMenus($menus, &$seenIds = []) { $uniqueMenus = []; foreach ($menus as $menu) { // 如果当前菜单id已经见过,跳过 if (in_array($menu['id'], $seenIds)) { continue; } $seenIds[] = $menu['id']; // 递归处理子菜单 if (!empty($menu['childs'])) { $menu['childs'] = removeDuplicateMenus($menu['childs'], $seenIds); } $uniqueMenus[] = $menu; } return $uniqueMenus; } // 使用方法 $originalMenus = App\Menu::with(['childs'])->get()->toArray(); $uniqueMenus = removeDuplicateMenus($originalMenus);
基于url去重的调整
如果你的重复判断依据是url而不是id,只需要把上面的$menu['id']换成$menu['url'],$seenIds改成$seenUrls即可:
function removeDuplicateMenusByUrl($menus, &$seenUrls = []) { $uniqueMenus = []; foreach ($menus as $menu) { if (in_array($menu['url'], $seenUrls)) { continue; } $seenUrls[] = $menu['url']; if (!empty($menu['childs'])) { $menu['childs'] = removeDuplicateMenusByUrl($menu['childs'], $seenUrls); } $uniqueMenus[] = $menu; } return $uniqueMenus; }
三、额外提示
- 如果你用的是Laravel的集合,也可以利用集合的递归方法来处理,比如
flatMap结合unique,但嵌套结构下递归函数更直观。 - 最好先排查为什么会出现重复数据:是数据库里本身有重复的记录?还是关联查询的逻辑问题?从根源解决会更高效。
内容的提问来源于stack exchange,提问作者Hula Hula
相关产品推荐
相关产品推荐

