js两个数组(包含子项),当数组A包含元素a时,只保留a,去除a同级元素


数据数组LiItem:

    
    [
            {
                text: '长沙',
                className: '长沙',
                children:[
                    {text:'全部',id:'全部',province:'changsha'},
                    {text:'浏阳市',id:'浏阳市',province:'changsha'}, 
                    {text:'长沙县',id:'长沙县',province:'changsha'}, 
                    {text:'芙蓉区',id:'芙蓉区',province:'changsha'},
                    {text:'雨花区',id:'雨花区',province:'changsha'}, 
                    {text:'天心区',id:'天心区',province:'changsha'}, 
                    {text:'岳麓区',id:'岳麓区',province:'changsha'},
                    {text:'望城区',id:'望城区',province:'changsha'},
                    {text:'宁乡市',id:'宁乡市',province:'changsha'},
                ]
            },{
                text: '湘潭',
                className: '湘潭',
                children:[
                    {text:'湘潭县',id:'湘潭县',province:'xiangtang'},
                    {text:'岳塘区',id:'岳塘区',province:'xiangtang'},
                    {text:'雨湖区',id:'雨湖区',province:'xiangtang'},
                    {text:'韶山市',id:'韶山市',province:'xiangtang'},
                    {text:'湘乡市',id:'湘乡市',province:'xiangtang'},
                ]
            },{
                text: '益阳',
                className: '益阳',
                children:[
                    {text:'南县',id:'南县',province:'yiyang'},
                    {text:'沅江市',id:'沅江市',province:'yiyang'},
                    {text:'资阳区',id:'资阳区',province:'yiyang'},
                    {text:'赫山区',id:'赫山区',province:'yiyang'},
                    {text:'桃江县',id:'桃江县',province:'yiyang'},
                    {text:'安化县',id:'安化县',province:'yiyang'},
                ]
            },
        ]

选择的是绑定的是children下的id内容,当id选中全部时,第一个children只保留'全部',把选中的第一个children下的地区都删除,我现在只做到点击时添加禁止选择;实际还是选中id,比如

选中的数组为

areaIds:['长沙县', '芙蓉区', '全部', '雨湖区', '岳塘区'] 

想将选中数组调整为目标数组:

areaIds:['全部', '雨湖区', '岳塘区']

我用的是vant的分类选择van-tree-select

现在主要想如果选择点击“全部”,把长沙下的选择项删除,只保留全部,其他的地区可以继续添加

阅读 1.9k
2 个回答

这里有一个隐藏的问题:

既然“全部”和其他选项从形式上没有本质区别,在选择结果里它又不是第 1 个,那是根据什么来决定留下“全部”而不是“长沙县”和“芙蓉区”呢?是因为它是在 LiItem 中是所属数组的第一个元素吗?

第二个问题,“雨湖区”和“岳糖区”是在同一个数组中,为什么又要两个都保留呢?

从结果来分析,只能得到这样一个规则:如果选了“全部”,不就选“长沙”子数组中的其他元素。那么“全部”就是一个特殊元素,针对它来处理就好。不过很容易想到这里存在一个隐藏的问题 —— 其它 children 中也有“全部”该怎么区分?

只根据当前的情况来去除“全部”的兄弟项是很容易的

let areaIds = ["长沙县", "芙蓉区", "全部", "雨湖区", "岳塘区"];

// 如果有全部,就把长沙中除全部之外的项去掉
if (areaIds.includes("全部")) {
    const set = new Set(liItems.find(it => it.className === "长沙").children.map(({ id }) => id));
    set.delete("全部");
    areaIds = areaIds.filter(it => !set.has(it));
}

也可以根据这个规则写一个通用函数

/**
 * 
 * @param {*} ids 当前结果 ID 集(需要去除某些项)
 * @param {*} data 数据源
 * @param  {...any} exclusives 需要根据这些主 ID 排除其兄弟项
 * @returns 
 */
function removeExclusive(ids, data, ...exclusives) {
    // 先根据需要排除的主 ID 生成一张映射表,键为该 ID,值为其兄弟 ID 集
    const entries = exclusives.map(id => {
        // 找到这个 ID 所在的 item
        const item = data.find(({ children }) => children.some(({ id: cid }) => cid === id));
        // 从这个 item 的 children 生成 ID 集
        const childrenIds = new Set(item?.children.map(({ id }) => id) ?? []);
        // 去掉主 ID
        childrenIds.delete(id);
        // 生成 entry,便于后面用 fromEntries 生成映射表
        return [id, childrenIds];
    });
    const dict = Object.fromEntries(entries);

    // 对每个 ID 去查表,在表里的,就去掉
    return ids.filter(id => !dict[id]?.has(id));
}

console.log(removeExclusive(areaIds, liItems, "全部"));
已参与了 SegmentFault 思否社区 10 周年「问答」打卡 ,欢迎正在阅读的你也加入。

没必要这么做的。你一定要保存所有id,然后是否显示为你说的效果是前端逻辑判断的结果,毕竟后端不可能为此还增加一个全部选中的属性。(万一长沙新增了一个区怎么办?)何况,他又不想全选了怎么取消也是要考虑的。所以,各种用户方便的操作都代表更复杂的逻辑,而不是可以偷懒少写几行代码。

已参与了 SegmentFault 思否社区 10 周年「问答」打卡 ,欢迎正在阅读的你也加入。

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题