js map遍历修改嵌套数组失败

如下方代码所示,我想把赵一、赵二、赵四的hasFinished设置为true,但是打印的结果依然是原始数据,请问是哪里出了什么问题吗?

        let moduleId = 2;
        let sourceId = 8;
        let value = ['赵一','赵二','赵四']
        let moduleData = JSON.parse(JSON.stringify([
            {
                "id": 2,
                "name": "镇长一",
                "subordinate": [
                    {
                        "id": 8,
                        "name": "村长一",
                        "subordinate": [],
                        "childList": [
                            {
                                "id": 20,
                                "name": "赵一",
                                "hasFinished":true
                            },
                            {
                                "id": 21,
                                "name": "赵二",
                                "hasFinished":false
                            },
                            {
                                "id": 23,
                                "name": "赵三",
                                "hasFinished":false
                            },
                            {
                                "id": 35,
                                "name": "赵四",
                                "hasFinished":false
                            }
                        ]
                    }
                ],
                "childList":[]
            },
            {
                "id": 4,
                "name": "镇长二",
                "subordinate": [
                    {
                        "id": 5,
                        "name": "村长二",
                        "subordinate": [],
                        "childList": [
                            {
                                "id": 24,
                                "name": "钱一",
                                "hasFinished":false
                            }
                        ]
                    }
                ],
                "childList":[]
            },
            {
                "id": 9,
                "name": "镇长三",
                "subordinate": [
                    {
                        "id": 11,
                        "name": "村长三",
                        "subordinate": [],
                        "childList": [
                            {
                                "id": 36,
                                "name": "孙一",
                                "hasFinished":false
                            }
                        ]
                    }
                ],
                "childList":[]
            },
            {
                "id": 10,
                "name": "镇长四",
                "subordinate": [],
                "childList":[]
            }
        ]));
        let arr = moduleData.map(m => {
            if(moduleId === m.id) {
                m.subordinate.map(s => {
                    if(sourceId === s.id) {
                        s.childList.map(c => {
                            if (value.includes(c.name)) {
                                const obj = { ...c };
                                obj.hasFinished = true;
                                return obj
                            }
                            return c
                        })
                    }
                    return s
                })
            }
            return m
        })
        console.log('修改数据', arr);
阅读 3.5k
4 个回答
s.childList.map(c => {
                            if (value.includes(c.name)) {
                                const obj = { ...c };
                                obj.hasFinished = true;
                                return obj
                            }
                            return c
                        })

s.childListmap出一个新数组没有重写;改成这样s.childList = s.s.childList.map
ps: 不如用forEach直接修改旧数据,如果不想修改到旧数据上级也需要拷贝一个新的

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

为什么要用 map 去遍历数据呢? map 一般是希望要使用返回值,看到你没有使用,那么改成 forEach 也是可以的。

看到最内层,可以发现你这里是切断引用。那么外面应该有 s.childList.map = s.childList.map 之类的操作,当然上面的每层都需要有。

const obj = { ...c };
obj.hasFinished = true;
return obj

但是你最外面有深拷贝,那么后面的数据你完成可以使用引用管理来改啊。

在说回去你比较的条件是 id,那么 find 是不是更适合你?


let moduleId = 2;
let sourceId = 8;
let value = ['赵一','赵二','赵四']
let moduleData = JSON.parse(JSON.stringify([
    {
        "id": 2,
        "name": "镇长一",
        "subordinate": [
            {
                "id": 8,
                "name": "村长一",
                "subordinate": [],
                "childList": [
                    {
                        "id": 20,
                        "name": "赵一",
                        "hasFinished":true
                    },
                    {
                        "id": 21,
                        "name": "赵二",
                        "hasFinished":false
                    },
                    {
                        "id": 23,
                        "name": "赵三",
                        "hasFinished":false
                    },
                    {
                        "id": 35,
                        "name": "赵四",
                        "hasFinished":false
                    }
                ]
            }
        ],
        "childList":[]
    },
    {
        "id": 4,
        "name": "镇长二",
        "subordinate": [
            {
                "id": 5,
                "name": "村长二",
                "subordinate": [],
                "childList": [
                    {
                        "id": 24,
                        "name": "钱一",
                        "hasFinished":false
                    }
                ]
            }
        ],
        "childList":[]
    },
    {
        "id": 9,
        "name": "镇长三",
        "subordinate": [
            {
                "id": 11,
                "name": "村长三",
                "subordinate": [],
                "childList": [
                    {
                        "id": 36,
                        "name": "孙一",
                        "hasFinished":false
                    }
                ]
            }
        ],
        "childList":[]
    },
    {
        "id": 10,
        "name": "镇长四",
        "subordinate": [],
        "childList":[]
    }
]));
// let arr = moduleData.map(m => {
//     if(moduleId === m.id) {
//         m.subordinate.map(s => {
//             if(sourceId === s.id) {
//                 s.childList.map(c => {
//                     if (value.includes(c.name)) {
//                         const obj = { ...c };
//                         obj.hasFinished = true;
//                         return obj
//                     }
//                     return c
//                 })
//             }
//             return s
//         })
//     }
//     return m
// })
moduleData
    ?.find(v=>v.id == moduleId)
    ?.subordinate
    ?.find(v=>v.id == sourceId)
    ?.childList
    ?.forEach(v=>{
        if (value.includes(v.name)) {
            v.hasFinished = true;
        }
    })
console.log('修改数据', moduleData[0].subordinate[0].childList.map(v=>v.hasFinished));
已参与了 SegmentFault 思否社区 10 周年「问答」打卡 ,欢迎正在阅读的你也加入。
s.childList.map(c => {
                            if (value.includes(c.name)) {
                                const obj = { ...c };
                                obj.hasFinished = true;
                                return obj
                            }
                            return c
                        })

obj虽然改了,但是你只是个map有啥用,等效代码

const arr = [1, 2, 3];
arr.map(item => item * 2);
console.log(arr);

你说arr会变成[2, 4, 6]吗?

s.childList = s.childList.map ...

哦,发完答案发现别人也答了,那再补两句吧。
真的知道自己在干什么,而不是无脑复制粘贴。你这个逻辑根本不需要return,直接改就行了,写那么多废话还错了,冤枉吗。

         moduleData.map(m => {
            if(moduleId === m.id) {
                m.subordinate.map(s => {
                    if(sourceId === s.id) {
                        s.childList.map(c => {
                            if (value.includes(c.name)) {
                                c.hasFinished = true;
                            }
                        })
                    }
                })
            }
        });
        console.log(moduleData);

这不就完了吗。如果你不想直接改moduleData,那一样const arr = JSON.parse(JSON.stringify(moduleDate));然后直接对arr做就好了。

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

层次结构固定,查找顺序也明确,

// 第一层中查找 moduleId 匹配的
moduleData.find(({ id }) => id === moduleId)
    // 如果找到了,在其第二层中查找 sourceId
    ?.subordinate?.find(({ id }) => id === sourceId)
    // 如果找到了,在第三层中找出 value 中列了的人
    ?.childList?.filter(({ name }) => value.includes(name))
    // 如果找到了,对它们进行处理,给 hasFinished 赋 true
    ?.forEach(it => it.hasFinished = true);

这里 ?. 表示,如果非空则执行。如果 subordinatechildList 保存存在且不为 null,那它们后面的 ? 可以不要,直接 .。但是每行一开始的那个 ? 是需要的,除非能 保证每次查找一定能找到某个对象(查找结果不会是 undefined)。


之前的回答是没看清楚问题,留在这里仅供参考
// 因为结果是固定的,直接用两层展开,把所有“赵四”那一级的对象找出来,放在一个数组中
moduleData.flatMap(({ subordinate }) => subordinate?.flatMap(it => it?.childList ?? []) ?? [])
    // 根据 value 列表中提供的名字过滤出需要处理的那几个
.filter(({ name }) => value.includes(name))
// 遍历处理之
.forEach(it => it.hasFinished = true);

性能不是最好的,但是逻辑肯定很清楚。

如果层次结构不确定,比如每层的 subordinatechildList 都要去遍历,那么可以用递归

function flatSpecially(list) {
if (!list?.length) {
  return [];
}

return [
  ...list,
  ...list.flatMap(
      ({ subordinate = [], childList = [] }) => flatSpecially([...subordinate, ...childList])
  )
];
}

// 递归找出所有项,然后再来过滤 + 处理
flatSpecially(moduleData)
.filter(({ name }) => value.includes(name))
.forEach(it => it.hasFinished = true);

已参与了 SegmentFault 思否社区 10 周年「问答」打卡 ,欢迎正在阅读的你也加入。
撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题