简体   繁体   English

如何将对象列表简化为重复项

[英]how to reduce a list of objects to duplicates

[edited for clarity] [为清楚起见而编辑]

Using lodash, given an array of objects: 使用lodash,给定对象数组:

var v = [{'a':1, 'b':1}, {'a':1, 'b':2}, {'a':1, 'c':1}];

how do I return an object which is the intersection of those objects (both key and value)? 我如何返回一个对象,这些对象是这些对象(键和值)的交集? In this case: 在这种情况下:

{'a':1}

I am looking for key value pairs which are in every object. 我正在寻找每个对象中的键值对。


This seems like a task for _.reduce , but I am not sure how to find the object duplicates. 这似乎是_.reduce的任务,但是我不确定如何查找对象重复项。

Indeed you can use Array#reduce with a hash object, and Object#keys to get all key:value pairs that appear in all objects. 实际上,您可以将Array#reduce与哈希对象一起使用,并使用Object#keys来获取出现在所有对象中的所有键:值对。

 var v = [{'a':1, 'b':1}, {'a':1, 'b':2}, {'a': 1, 'c':1, 'b': 1}]; var hashCount = {}; var result = v.reduce(function(r, o) { Object.keys(o).forEach(function(key) { // iterate the object keys var hash = key + '_' + o[key]; // create the hash from the key:value pair hashCount[hash] = (hashCount[hash] || 0) + 1; // increment the hash in the hashCount // add the pair to the result when the hash count number is equal to the length of the array, if(hashCount[hash] === v.length) { r[key] = o[key]; } }); return r; }, {}); console.log(result); 

btw - my original answer was written to cope with the case of a property that appears in at at least two objects. 顺便说一句 -我最初的回答是为了应对至少出现在两个对象中的属性的情况。 So, if you just want to find a property that appears in 2 objects (or any arbitrary number), change this line if(hashCount[hash] === v.length) { to if(hashCount[hash] === 2) { 因此,如果您只想查找出现在2个对象(或任意数字)中的属性,请将此行if(hashCount[hash] === v.length) {更改为if(hashCount[hash] === 2) {

You could reduce the objects by iterating the keys and checking the values. 您可以通过迭代键并检查值来减少对象。 Then build a new object and return. 然后建立一个新的对象并返回。

 var array = [{ a: 1, b: 1 }, { a: 1, b: 2 }, { a: 1, c: 1 }], result = array.reduce(function (a, b) { return Object.keys(a).reduce(function (o, k) { if (a[k] === b[k]) { o[k] = a[k]; } return o; }, {}); }); console.log(result); 

ES6 with Object.assign ES6与Object.assign

 var array = [{ a: 1, b: 1 }, { a: 1, b: 2 }, { a: 1, c: 1 }], result = array.reduce((a, b) => Object.keys(a).reduce((o, k) => Object.assign(o, a[k] === b[k] ? { [k]: a[k] } : {}), {})); console.log(result); 

Here's one way to do it with loDash 这是使用loDash的一种方法

 var v = [{'a':1, 'b':1}, {'a':1, 'b':2}, {'c':1}]; let k = _.chain(v).map(Object.entries) .flatten(true) .filter((f,u,n) => { let i = _.findLastIndex(n, z => (_.isEqual(f,z))); return n.some((g,o) => (_.isEqual(f,g) && u!==o && u===i)); }) .map(x => ({[x[0]]:x[1]})) .value() console.log( k ) 
 <script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.min.js"></script> 

It maps the objects back split up, then flattens it out, then filters based on object equality, using _.isEqual , not string comparison, then maps back the object and get the value from the chain 它将对象映射回拆分,然后将其展平,然后使用_.isEqual而不是字符串比较基于对象相等性进行过滤,然后将对象映射回并从链中获取值

Solution using _.reduce : 使用_.reduce解决方案:

var r = _.reduce(v.slice(1), function (result, item) {
    Object.keys(result).forEach(function (key) {
      if (!item.hasOwnProperty(key)) delete result[key];
    })
    return result;
  }, Object.assign({}, v[0]));

The idea is to use one item as the result, which is handed over to reduce() as third parameter (accumulator). 想法是使用一项作为结果,将其交付给reduce()作为第三个参数(累加器)。 That is done by copying the Object, since that is modified within the algorithm, like so: Object.assign({}, v[0]) . 这是通过复制Object来完成的,因为它是在算法中修改的,例如: Object.assign({}, v[0])

Within the reduce function (second parameter) we check if the actual item for each Property of the result. 在reduce函数(第二个参数)中,我们检查结果的每个Property是否为实际项目。 If the item does not have it, we remove it from the result. 如果没有该项目,则将其从结果中删除。

Since the first item of the list is already given to the function, it can be excluded from the array to reduce, what is done by v.slice(1) . 由于列表的第一项已经提供给该函数,因此可以将其从数组中排除以进行减少,这可以通过v.slice(1)

Why does that work: 为什么行得通:

  1. Each item in the list can be used as the initial intersection, since we are looking for all properties that exist in all objects, we can safely say that we do not forget to include any other properties from any other object. 列表中的每个项目都可以用作初始交集,因为我们正在寻找所有对象中都存在的所有属性,因此可以放心地说,我们不会忘记包括来自任何其他对象的任何其他属性。

  2. If an item does not have any property which is part of the intersection, that property is not part of the intersection and needs to be removed from there. 如果某项不具有作为相交的一部分的任何属性,则该属性不属于相交的一部分,需要从那里删除。

Note: 注意:

One downside of using reduce here is: It iterates over each item in the list, no matter if the intersection is already empty, where the algorithm could stop. 这里使用reduce的一个缺点是:迭代列表中的每个项目,无论交集是否已经为空,算法都可以在哪里停止。 So writing a regular function like the one below might be faster for large lists of objects, which are likely to have no intersection: 因此,对于大型对象列表(可能没有交集),编写如下所示的常规函数​​可能会更快:

function intersect (list) {
  var 
    remain = [].concat(list),
    result = Object.assign({}, remain.pop()),
    keys = Object.keys(result),
    item;

  while ((item = remain.pop()) != undefined && keys.length > 0) {
    keys.forEach(function (key) {
      if (!item.hasOwnProperty(key)) delete result[key];
    });

    keys = Object.keys(result);
  }

  return result;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM