【问题标题】:Summerize Array with Arrays in Objetcs with different Labels用不同标签的对象中的数组汇总数组
【发布时间】:2020-01-07 14:11:05
【问题描述】:

我有一个包含对象的数组,其中一些具有相同的“子数组”但具有不同的标签。我需要根据平等来总结一下。

到目前为止,比较两个不同的数组很容易,但我不确定如何在同一个数组中进行比较。 这是我的阵列。

let array = [{
  label: 'sting1',
  subArray: ['A', 'B']
}, {
  label: 'sting2',
  subArray: ['B']
}, {
  label: 'sting3',
  subArray: ['A', 'B']
}]

如您所见,标有String1String3 的对象具有相同的数组。所以现在我需要总结一下。我需要的结果是这样的:

let result = [{
  label: ['sting1', 'string3'],
  subArray: ['A', 'B']
}, {
  label: ['sting2'],
  subArray: ['B']
}]

【问题讨论】:

    标签: javascript arrays dictionary filter reduce


    【解决方案1】:

    这个问题很可能是一个通用的“分组依据”,尽管它涉及一些问题,主要是:

    • 您需要按项目数组进行分组。因为您没有通过引用或值指定威胁的数组是否相同,所以我们需要假设您可能需要单独比较每个值。
    • 关于上述内容,我假设您的数组仅由原语组成。
    • 标签似乎在某种程度上按出现顺序排列,因此我假设您希望它们按在原始数组中出现的相同顺序进行收集

    也就是说,您可以使用Array.reduce 遍历原始数组,并逐步:

    • 检查累加器是否已经具有所需的键(不幸的是,在您的场景中,它无法通过简单的查找来完成。从技术上讲,您可以使用 Wea​​kMap,但这会稍微复杂一些)。在您的情况下,关键是您的子数组。因此,如果累加器中有任何项目具有当前循环的 subArray,您只需将标签推送到现有的。
    • 如果没有,您只需推送当前循环的项目,假设您不关心引用。如果您不希望更改原始的 array 值,请先深度复制它们
    • 一切完成后,只需返回累加器并继续操作,直到完成。

    下面是一个示例代码专为使用您发布的场景而构建

    let array = [{
      label: 'sting1',
      subArray: ['A', 'B']
    }, {
      label: 'sting2',
      subArray: ['B']
    }, {
      label: 'sting3',
      subArray: ['A', 'B']
    }];
    
    const grouped = array.reduce((acc, next) => {
      const { subArray } = next; // <-- acquire the subArray of the currently looped item.
      const exists = acc.find(({subArray: registeredSubArray}) => registeredSubArray.every(i => subArray.indexOf(i) > -1));
      // ^-- checks whether, in the accumulator, said subarray exists and if every item of the subarray is matched through indexOf against the accumulator looped item.
      if (exists) exists.label.push(next.label); // <-- if the item already exists, simply collect the label.
      else acc.push(Object.assign(next, {label: [next.label]})); // <-- otherwise, create a new object and assign the label as an array of labels instead of a single label.
      return acc; // finally, return the accumulator and continue.
    }, []);
    
    console.log(grouped);

    【讨论】:

    • 哇,我不知道该说什么。感谢您的大量回答!我也想将您的答案标记为解决方案。您做了很多工作来描述如何使用reduce 及其行为的背景。但是 Stackoverflow 只允许一个答案作为解决方案。但我知道我将来需要大量使用你的答案,我可以从你那里学到很多东西。谢谢你的辛劳工作!它真的让我明白了 reduce 的作用,这是我过去从未理解过的。您比 Mozillas 文档更好地描述了这一点 - 抽象但实用。非常感谢!! :D
    • @equaliser 谢谢,我很高兴这有帮助。作为旁注,在我看来,Nenad 上面的答案比我的答案稍微聪明一些,因为他考虑过通过唯一生成的密钥进行查找,这比我的方法更有效(在reduce,虽然他的解决方案随着时间的推移变得不那么复杂,但如果我没记错的话,应该是 O(log n)。总的来说,我认为 Nenad 的解决方案是目前最好的解决方案,只是想指出这一点,尽管我很高兴你喜欢我的考虑;)
    【解决方案2】:

    假设您的 subArray 仅包含原始类型,因此没有对象、数组或函数,您可以使用 sortjoin 方法创建键,然后使用 reduce 构建结果。

    let array = [{"label":"sting1","subArray":["A","B"]},{"label":"sting2","subArray":["B"]},{"label":"sting3","subArray":["A","B"]}]
    
    const map = array.reduce((r, e) => {
      const key = e.subArray.sort().join('-');
      if (!r.has(key)) r.set(key, { ...e, label: [e.label]});
      else r.get(key).label.push(e.label);
      return r;
    }, new Map)
    
    const result = [...map.values()];
    console.log(result)

    【讨论】:

      【解决方案3】:

      只是为了给你一个没有 map 和 reduce 的选项。在学习 javascript 时,我通常更喜欢总是回到简单的语法来了解发生了什么。

      如果有帮助,请告诉我!

      let array = [{label: 'sting1', subArray: ['A', 'B']}, {label: 'sting2',subArray: ['B']}, {label: 'sting3', subArray: ['A', 'B']}]
      
      result = [];
      checkdups = [];
      
      for (i = 0; i < array.length; i++) {
          data = {
              label: [array[i].label],
              subArray: array[i].subArray
          }
      
          for (var j = 0; j < array.length; j++) {
      
              if (String(array[j].subArray) == String(array[i].subArray) && i != j) {
                  data.label.push(array[j].label);
              }
          }
      
          if (checkdups.indexOf(String(array[i].subArray))==-1) {
              checkdups.push(String(array[i].subArray));
              result.push(data);
          }
      
      }
      
      console.log(result);

      【讨论】:

      • yapp,你是绝对正确的!从技术上了解背景。谢谢你的工作。我永远不会想到以这种方式使用循环。非常感谢您的帮助! :D
      【解决方案4】:

      我们可以使用Array.prototype.reduce 遍历数组中的每个对象,并通过匹配子数组对其进行分组。

      我们可以使用Array.prototype.findIndex找到匹配的子数组,如果找到我们添加到现有的标签数组中,否则创建一个新的标签数组并将对象推送到最终数组中:

      const array = [{"label":"sting1","subArray":["A","B"]},{"label":"sting2","subArray":["B"]},{"label":"sting3","subArray":["A","B"]}];
      
      const res = array.reduce((acc, obj) => {
        const idx = acc.findIndex(o => (o.subArray.length === obj.subArray.length) 
                            && o.subArray.every(e => obj.subArray.includes(e)));
        if(idx >= 0){
           acc[idx].label.push(obj.label);
        }else{
           obj.label = [obj.label];
           acc.push(obj);
        }
        return acc;
          
      }, []);
      console.log(res);

      【讨论】:

      • 我在想,reduce 原型会帮助我,但老实说,因为现在我没有更深入地研究 reduce,所以我永远不会有使用这个的想法大大地。您的解决方案非常简单易懂。我希望我也可以将您的答案标记为解决方案,但 stackoverflow 不允许我这样做。但我需要大量使用你的方法。你用Reduce让我大开眼界,非常感谢! :D
      • @equaliser 谢谢你的美言!我希望您也选择我的答案,但请选择对您最有帮助的答案。
      猜你喜欢
      • 2019-02-27
      • 2019-01-04
      • 1970-01-01
      • 2014-11-25
      • 2016-11-21
      • 2015-11-16
      • 2015-07-10
      • 1970-01-01
      • 2018-06-23
      相关资源
      最近更新 更多