js两个数组的数据处理

two
  • 5
新手上路,请多包涵

数组一:

arr1 = [
  {uid: 2},
  {uid: 3},
  {uid: 4}
]

数组二:

arr2 = [
  {  
    text: '随便1',
    children: [
      {uid: 1},
      {uid: 2}
    ]
  },
  {  
    text: '随便2',
    children: [
      {uid: 3},
      {uid: 4}
    ]
  },
  {  
    text: '随便3',
    children: [
      {uid: 5},
      {uid: 6}
    ]
  }
]

想得到的效果

arr2 = [
  {  
    text: '随便1',
    children: [
      {uid: 1, checked: false},
      {uid: 2, checked: true}
    ]
  },
  {  
    text: '随便2',
    children: [
      {uid: 3, checked: true},
      {uid: 4, checked: true}
    ]
  },
  {  
    text: '随便3',
    children: [
      {uid: 5, checked: false},
      {uid: 6, checked: false}
    ]
  }
]

数组arr1 中的uid的值对应在数组arr2 中添加属性 checked: true, 反之就是false。
请大神们帮我看看这个问题... 万分感谢。

回复
阅读 754
7 个回答
✓ 已被采纳
const arr1 = [
    { uid: 2 },
    { uid: 3 },
    { uid: 4 }
];
const arr2 = [
    {
        text: '随便1',
        children: [
            { uid: 1 },
            { uid: 2 }
        ]
    },
    {
        text: '随便2',
        children: [
            { uid: 3 },
            { uid: 4 }
        ]
    },
    {
        text: '随便3',
        children: [
            { uid: 5 },
            { uid: 6 }
        ]
    }
];

arr2.forEach(value => {
    value.children.forEach(children => {
        children.checked = arr1.map(a => a.uid).includes(children.uid);
    });
});
console.log(arr2);

@边城 的答案提到深拷贝麻烦,其他答案目前也没有深拷贝的写法,我写一个作为补充,好像也不是很麻烦,语义也很清晰:

arr2
  .map((item) => ({
    ...item,
    children: item.children
      .map((child) => ({
        ...child,
        checked: arr1.some(({uid}) => uid === child.uid),
      })),
  }));

主要是使用扩展运算符(...)Array.prototype.map。这两者都只是浅拷贝,但 arr2 只有 4 层,要操作的数据正好在最深层,所以这两者正好用 4 次完成深拷贝。

map 单独换了一行,这样应该更能体现嵌套关系。

arr2.forEach((item, index) => {
    const uid = arr1[index].uid
    item.children.forEach(i => {
        if (i.uid === uid) {
            i.checked = true
        } else {
            i.checked = false
        }
    })
})

分析

首先,这个真不需要用递归,因为不存在多级相同结构数据的情况。

说起来,其实就是遍历所有 children 项,然后去 arr1 里查 uid,根据查询结果添加 checked 属性。

所以可以预见是两层循环,第一层遍历 arr2 的所有元素,第二层对每个元素遍历其 children

接下来要解决怎么查询的问题。遍历 children 的时候,每个 childuid 是拿得到的,这个就是要查询的值。

而要在 arr1 里去查询,无非几种办法:

  1. 遍历法,可以用 Array 上定义的几个方法:some()find()/findIndex()includes() 等,

    // 假设用 childUid 来表示要查找的 uid
    
    arr1.some(it => it.uid === childUid);
    arr1.find(it => it.uid === childUid);
    arr1.findIndex(it => it.uid === childUid) >= 0;
    
    // 只有 includes 麻烦一点,因为要判断的东西必须是一个可以比较的值,而 {uid:1} === {uid:1} 会得到 false
    arr1.map(({uid}) => uid).includes(childUid);
当然,有兴趣还可以自己写 `for` 或者 `for ... in`/ `for ... of` 循环来查找,方法多的是。
  1. 查表法。当然是先把 arr1 变成一个表。因为只需要判断在/不在,所以不需要用 Map,用 Set 就好。注意 Set 对象只需要生成一次,然后在每个循环体里使用就成。下面的示例代码是使用查表法写的,所以不单独写代码示例。

示例代码

const r = (() => {
    const checkIds = new Set(arr1.map(it => it.uid));
    arr2.forEach(({ children = [] }) => {
        children.forEach(child => child.checked = checkIds.has(child.uid));
    });
    return arr2;
})();

有几点需要注意:

  1. 封 IIFE 主要是为了 checkIds 用后即毁,不污染环境
  2. 是直接在在 arr2 原数据上修改的,而不是产生的新对像(深拷贝麻烦,如果不是一定要保留的数组干净,就不考虑深拷贝了)

简单来说使用了递归

arr1 = [
  {uid: 2},
  {uid: 3},
  {uid: 4}
];
arr2 = [
  {  
    text: '随便1',
    children: [
      {uid: 1},
      {uid: 2}
    ]
  },
  {  
    text: '随便2',
    children: [
      {uid: 3},
      {uid: 4}
    ]
  },
  {  
    text: '随便3',
    children: [
      {uid: 5},
      {uid: 6}
    ]
  }
];
var setTreeCheck =(arr,checks)=>arr.map(item=>(item.uid && (item.checked=Boolean(checks.find(check=>item.uid == check.uid))),setTreeCheck(item.children || [],checks),item));
setTreeCheck(arr2,arr1)

结果

[
  {
    "text": "随便1",
    "children": [
      {
        "uid": 1,
        "checked": false
      },
      {
        "uid": 2,
        "checked": true
      }
    ]
  },
  {
    "text": "随便2",
    "children": [
      {
        "uid": 3,
        "checked": true
      },
      {
        "uid": 4,
        "checked": true
      }
    ]
  },
  {
    "text": "随便3",
    "children": [
      {
        "uid": 5,
        "checked": false
      },
      {
        "uid": 6,
        "checked": false
      }
    ]
  }
]

我个人还是喜欢用最纯粹的方法来解各种算法问题,虽然用数组方法会更方便,但这有利于对解题思路和逻辑的理解,无论是map、forEach或是some,底层都离不开一层循环,归根结底还是要遍历数组,然后判断uid的值是否存在于另一个数组中,再把checked值添加进对象。

var tmp = [];
for (var i = arr1.length; i--; tmp.unshift(arr1[i].uid));
for (var i = 0; i < arr2.length; ++i) {
    for (var j = 0; j < arr2[i].children.length; ++j) {
        var child = arr2[i].children[j];
        child.checked = tmp.indexOf(child.uid) >= 0
    }
}
console.dir(arr2);
   let uids = arr1.map(item => {
      return item.uid
    })
    arr2.forEach(item => {
      item.children.forEach(childitem => {
        Object.assign(childitem, {
          checked: uids.includes(childitem.uid)
        })
      })
    })
    console.log(arr2)
撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
你知道吗?

宣传栏