【问题标题】:Merge duplicate objects in array of objects合并对象数组中的重复对象
【发布时间】:2015-05-04 08:42:50
【问题描述】:

我有以下对象数组,

var data = [
    {
        label: "Book1",
        data: "US edition"
    },
    {
        label: "Book1",
        data: "UK edition"
    },
    {
        label: "Book2",
        data: "CAN edition"
    }
];

我想根据属性“标签”合并重复的对象 这样最终输出将如下所示,

var data = [
    {
        label: "Book1",
        data: ["US edition", "UK edition"] //data attribute is merged
    },
    {
        label: "Book2",
        data: "CAN edition"
    }
];

有人可以帮我确定方法吗?

【问题讨论】:

标签: javascript arrays


【解决方案1】:

我可能会循环使用filter,沿着这些线跟踪我以前见过的对象的地图(编辑以反映你同意是的,总是让(entry).data 有意义一个数组):

var seen = {};
data = data.filter(function(entry) {
    var previous;

    // Have we seen this label before?
    if (seen.hasOwnProperty(entry.label)) {
        // Yes, grab it and add this data to it
        previous = seen[entry.label];
        previous.data.push(entry.data);

        // Don't keep this entry, we've merged it into the previous one
        return false;
    }

    // entry.data probably isn't an array; make it one for consistency
    if (!Array.isArray(entry.data)) {
        entry.data = [entry.data];
    }

    // Remember that we've seen it
    seen[entry.label] = entry;

    // Keep this one, we'll merge any others that match into it
    return true;
});

在 ES6 环境中,我会使用 seen = new Map() 而不是 seen = {}

注意Array.isArray 是由 ES5 定义的,所以一些比较老的浏览器比如 IE8 不会有它。不过,它可以很容易地填充/填充:

if (!Array.isArray) {
    Array.isArray = (function() {
        var toString = Object.prototype.toString;
        return function(a) {
            return toString.call(a) === "[object Array]";
        };
    })();
}

旁注:我可能还会总是entry.data 设为一个数组,即使我没有看到它的两个值,因为一致的数据结构更容易处理.我没有在上面这样做,因为当只有一个匹配条目时,您的最终结果显示 data 只是一个字符串。 (我们现在已经在上面完成了。)

现场示例(ES5 版本):

var data = [
    {
        label: "Book1",
        data: "US edition"
    },
    {
        label: "Book1",
        data: "UK edition"
    },
    {
        label: "Book2",
        data: "CAN edition"
    }
];
snippet.log("Before:");
snippet.log(JSON.stringify(data, null, 2), "pre");
var seen = {};
data = data.filter(function(entry) {
    var previous;

    // Have we seen this label before?
    if (seen.hasOwnProperty(entry.label)) {
        // Yes, grab it and add this data to it
        previous = seen[entry.label];
        previous.data.push(entry.data);

        // Don't keep this entry, we've merged it into the previous one
        return false;
    }

    // entry.data probably isn't an array; make it one for consistency
    if (!Array.isArray(entry.data)) {
        entry.data = [entry.data];
    }

    // Remember that we've seen it
    seen[entry.label] = entry;

    // Keep this one, we'll merge any others that match into it
    return true;
});
snippet.log("After:");
snippet.log(JSON.stringify(data, null, 2), "pre");
<!-- Script provides the `snippet` object, see http://meta.stackexchange.com/a/242144/134069 -->
<script src="http://tjcrowder.github.io/simple-snippets-console/snippet.js"></script>

【讨论】:

  • 多么优化和精湛的逻辑:)
  • data如何获取重复数组的更新数组?
  • @FinbarMaginn:我不明白这个问题。
  • 我看不到如何将合并的数组分配给具有相同标签的返回数组项。另外我想知道这整个事情是否可以用 Map 和 Reduce 重写?
  • @FinbarMaginn: entry.data = [entry.data];previous = seen[entry.label]; previous.data.push(entry.data); 是做什么的。重新映射/减少:可能。不过,上面的简单循环不需要复杂性。
【解决方案2】:

尝试下面的方法,它可以找到并且很短

 var data = [
    {
        label: "Book1",
        data: "US edition"
    },
    {
        label: "Book1",
        data: "UK edition"
    },
    {
        label: "Book2",
        data: "CAN edition"
    }
];

const result = Array.from(new Set(data.map(s => s.label)))
    .map(lab => {
      return {
        label: lab,
        data: data.filter(s => s.label === lab).map(edition => edition.data)
      }
    })

console.log(result);

【讨论】:

  • 这很好用,但需要注意的是,如果你有更多的对象键,你需要找到一种方法来添加它们,我的解决方案...data.filter(f =&gt; f.label === lab)[0],
【解决方案3】:

我遇到了同样的情况,我希望在这里使用Set 而不是我的

const data = [
  {
    label: "Book1",
    data: "US edition"
  },
  {
    label: "Book1",
    data: "UK edition"
  },
  {
    label: "Book2",
    data: "CAN edition"
  },
  {
    label: "Book3",
    data: "CAN edition"
  },
  {
    label: "Book3",
    data: "CANII edition"
  }
];

const filteredArr = data.reduce((acc, current) => {
  const x = acc.find(item => item.label === current.label);
  if (!x) {
    const newCurr = {
      label: current.label,
      data: [current.data]
    }
    return acc.concat([newCurr]);
  } else {
    const currData = x.data.filter(d => d === current.data);
    if (!currData.length) {
      const newData = x.data.push(current.data);
      const newCurr = {
        label: current.label,
        data: newData
      }
      return acc;
    } else {
      return acc;
    }
    
  }
}, []);

console.log(filteredArr);

【讨论】:

    【解决方案4】:

    此代码在最新版本的 Firefox 上测试。要在其他浏览器上工作,请将库的 Array.isArray 更改为 lodash 或您喜欢的任何内容。

    var data = [
        {
            label: "Book1",
            data: "US edition"
        },
        {
            label: "Book1",
            data: "UK edition"
        },
        {
            label: "Book2",
            data: "CAN edition"
        }
    ],
    i = 0,
    j = data.length - 1,
    current;
    
    for (;i < data.length; i++) {
      current = data[i];
      for (;j > i; j--) {
         if (current.label === data[j].label) {
           if (Array.isArray(current.data)) {
             current.data = current.data.concat([data[j].data]);
           } else {
             current.data = [].concat([data[j].data, current.data]);
           }
           data.splice(j, 1);
         }
    
      }
    } 
    
    console.log(data);
    

    【讨论】:

      【解决方案5】:

      var data = [
          {
              label: "Book1",
              data: "US edition"
          },
          {
              label: "Book1",
              data: "UK edition"
          },
          {
              label: "Book2",
              data: "CAN edition"
          }
      ].filter(function(e, index ,b) {
          k = b.map(z => z.label)
          if(k.includes(e.label, index+1)){
              indexPosition = k.indexOf(e.label, index+1);
              b[indexPosition].data = (e.data+"||"+b[indexPosition].data);
              
          }else{
              e.data = e.data.split("||").length > 1 ? e.data.split("||") : e.data;
              return e;
          }
      });
      
      console.log(data)

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2018-03-14
        • 1970-01-01
        • 2019-07-15
        • 2019-04-10
        • 1970-01-01
        • 1970-01-01
        • 2021-11-12
        • 2020-07-26
        相关资源
        最近更新 更多