【问题标题】:Filter array based on another array and combined根据另一个数组过滤数组并合并
【发布时间】:2019-05-29 13:23:10
【问题描述】:

给定两个数组:

const inputOne = [
 {id: "valueA", prop: 123},
 {id: "valueB", prop: 456}
]

const inputTwo = [
 {id: "valueA", other: 54},
 {id: "valueB", other: 98},
 {id: "valueC", other: 11}
]

我正在尝试根据inputOneid 过滤inputTwo,然后合并两者中的属性。

期望的输出:

combinedAndFiltered = [
 {id: "valueA", other: 54, prop: 123},
 {id: "valueB", other: 98, prop: 456}
]

我尝试了mapfilter 和/或reduce 的各种组合,但不知何故无法理解。

【问题讨论】:

    标签: javascript arrays ecmascript-6


    【解决方案1】:

    您可以使用地图和过滤器来解决这个问题。如果我对你想要的东西没有错,这可能会有所帮助

    let result = inputOne.map((item) => {
        let leftItems = inputTwo.filter((item2) => {
            return item2.id == item.id
        }) 
        if(leftItems.length > 0) {
            return {id: item.id, other: leftItems[0].other, prop: item.prop}
            // or return {...item, ...leftItems[0]}
        }
    }).filter(item => !! item)
    

    【讨论】:

      【解决方案2】:

      此解决方案假设只有两个集合,id 属性在每个集合中是唯一的。

      您可以通过以下方式根据公共属性 (id) 创建两组合并对象的 an intersection

      • 迭代第一个集合,然后在第二个集合中搜索第一个集合的当前元素。
      • 如果找到匹配项,则将两个对象组合成一个新对象,然后将该对象添加到累加器集中。
      • 返回蓄能器

      此方法返回一个新集合,其中包含在两个集合中找到的合并对象。如果任一集合中缺少对象,则不会将其包含在输出中。

      const inputOne = [ {id: "valueA", prop: 123}, {id: "valueB", prop: 456}, {id: "valueD", prop: 789} ]
      const inputTwo = [ {id: "valueA", other: 54}, {id: "valueB", other: 98}, {id: "valueC", other: 11} ]
      
      function intersectAndMerge(a, b) {
        const accumulator = []
        for(let { id, ...props } of a) {
          const match = b.find(e =>  e.id === id)
          if(match !== undefined) {
            accumulator.push({ ...match, ...props })
          }
        }
        return accumulator
      }
      
      console.log(intersectAndMerge(inputOne, inputTwo))

      这也可以通过 reduce 循环来完成,但我发现它的可读性较差:

      const inputOne = [ {id: "valueA", prop: 123}, {id: "valueB", prop: 456}, {id: "valueD", prop: 789} ]
      const inputTwo = [ {id: "valueA", other: 54}, {id: "valueB", other: 98}, {id: "valueC", other: 11} ]
      
      function intersectAndMerge(a, b) {
        return a.reduce((accumulator, { id, ...props }) => {
          const match = b.find(e =>  e.id === id)
          if(match !== undefined) {
            accumulator.push({ ...match, ...props })
          }
          return accumulator
        }, [])
      }
      
      console.log(intersectAndMerge(inputOne, inputTwo))

      【讨论】:

        【解决方案3】:

        我认为几乎所有解决方案都将是某种双重迭代,无论是通过 for 循环、映射还是过滤器。与上面列出的方法不同的另一种方法是使用像 lodash 这样的库。 This answer already gives a pretty good example of the unionBy function.

        【讨论】:

          【解决方案4】:

          好吧,您可以先构建一个 Object 并使用它来获取其他值,同时使用 .map 迭代另一个数组

          const inputOne = [
           {id: "valueA", prop: 123},
           {id: "valueB", prop: 456}
          ],
           inputTwo = [
           {id: "valueA", other: 54},
           {id: "valueB", other: 98},
           {id: "valueC", other: 11}
          ],
          ids = {};
          
          inputTwo.forEach(function (o) {
              ids[o.id] = o;
          });
          
          var res = inputOne.map(function (o) {
              return {
                  other: ids[o.id].other,
                  prop: o.prop,
                  id: o.id
              };
          });
          
          console.log(res)

          【讨论】:

          • 如果第一个集合中的一个对象在第二个集合中不存在怎么办?
          【解决方案5】:

          这应该可以解决问题(假设inputOne 就像一个源);

          const inputOne = [
           {id: "valueA", prop: 123},
           {id: "valueB", prop: 456}
          ]
          
          const inputTwo = [
           {id: "valueA", other: 54},
           {id: "valueB", other: 98},
           {id: "valueC", other: 11}
          ]
          
          const mergeArrays = (first, second) => {
            return first.map((firstItem) => {
              const obj = second.find((secondItem) => secondItem.id === firstItem.id);
              return {...firstItem, ...obj};
            });
          };
          
          const combinedArrays = mergeArrays(inputOne, inputTwo);
          
          console.log(combinedArrays);

          【讨论】:

          • 如果第一个集合中的一个对象在第二个集合中不存在怎么办?
          【解决方案6】:

          使用Array.reduce()将第一个数组转换为Map,然后对第二个数组进行归约,如果在Map中找到对象,则从Map中获取对象,合并对象,添加到累加器中:

          const combine = (arr1, arr2) => {
            const arr1Map = arr1.reduce((m, o) => m.set(o.id, o), new Map)
            
            return arr2.reduce((r, o) => arr1Map.has(o.id) ? 
              [...r, { ...o, ...arr1Map.get(o.id) }] : r
            , [])
          }
          
          const inputOne = [{id: "valueA", prop: 123},{id: "valueB", prop: 456}]
          
          const inputTwo = [{id: "valueA", other: 54},{id: "valueB", other: 98},{id: "valueC", other: 11}]
          
          const result = combine(inputOne, inputTwo)
          
          console.log(result)

          【讨论】:

          • 为什么每次都创建一个新数组而不是推送到现有的累加器上?
          • 函数式编程不变性 - 累加器应该被一个没有改变的新值替换。如果你改变累加器,你正在使用 reduce 作为 for 循环。但是,正如您所提到的,这有点浪费,因此根据具体情况,您可能希望重用累加器(例如非常大的数组)。
          • 我认为这有点牵强。您是否有任何参考资料来支持该声明?为什么突变累加器是一件坏事?
          【解决方案7】:

          使用for的简单解决方案:

          // declare an empty array:
          let resArr = []
          
          for(i=0; i<inputOne.length; i++) {
            for(j=0; j<inputTwo.length; j++) {
              if(inputOne[i].id === inputTwo[j].id) {
                resArr.push({ ...inputOne[i], ...inputTwo[j] })
              }
            }
          }
          

          【讨论】:

          • 为什么在找到匹配项后继续迭代@​​987654323@?
          【解决方案8】:
          inputOne.map((a, i) => ({...a, ...inputTwo[i]}))
          

          假设 inputOne 是较短的,您可以使用if 来确保它在所有情况下都是正确的。

          编辑: 在真正实用的问题上,人们会在 haskell 中使用类似 zip 的东西,但是如果没有更多的库或自己实现它,恐怕你会被这样的东西困住。

          编辑 2:

          inputOne.map((a, i) => ({...a, ...(inputTwo.filter(x => x.id === a.id)[0])}))
          

          这基于id 属性工作,读起来不够好。

          【讨论】:

          • 此解决方案假定两个数组始终具有完全相同的索引。
          • 别担心,伙计! ?
          • 如果第一个集合中的一个对象在第二个集合中不存在怎么办?
          • 事情变得一团糟,但也有一个简单的解决办法,当然...inputOne.map((a, i) =&gt; ({...a, ...(inputTwo.filter(x =&gt; x.id === a.id)[0] || {})}))
          • 但据我所知,根据所提出的问题,只有两个集合中都存在的元素才应该输出。如果第二个集合中不存在该元素,该解决方案仍会输出该元素,只是不会合并它。
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2020-04-30
          • 2014-05-03
          • 2013-04-25
          • 2022-08-11
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多