简体   繁体   中英

Get array of unique elements from arrays of objects

i have arrays like the following ones:

files = [
       {name: 'Lorem', other: true},
       {name: 'Foo', other: true},
       {name: 'Bar', other: true}
      ];


files = [
       {name: 'Lorem', other: true},
       {name: 'Xxxxx', other: true}
      ];


files = [
       {name: 'Lorem', other: true},
       {name: 'Foo', other: true},
       {name: 'Epic', other: true},
       {name: 'Xxxxx', other: true}
      ];

I'm trying to get a merged array with unique elements with Underscore, but it doesn't work. Here is my code:

function getAllFiles(alldocs) {
  var all = [];

  _.each(alldocs, function(element) {
    all = _.union(all, element.files);
  });

  return all;
}

But i get an array with duplicate items.

Using underscore you can do it like this:

files1 = [
       {name: 'Lorem', other: true},
       {name: 'Foo', other: true},
       {name: 'Bar', other: true}
      ];


files2 = [
       {name: 'Lorem', other: true},
       {name: 'Xxxxx', other: true}
      ];


files3 = [
       {name: 'Lorem', other: true},
       {name: 'Foo', other: true},
       {name: 'Epic', other: true},
       {name: 'Xxxxx', other: true}
      ];
//append all  json
all = _.union(files1,files2, files3);
//get unique by define function which returns name (deciding factor for unique)
all = _.uniq(all, function(d){return d.name});
console.log(all);//prints the unique elements

working code here

A solution in plain Javascrift a temporary object and Array#forEach()

 var files1 = [{ name: 'Lorem', other: true }, { name: 'Foo', other: true }, { name: 'Bar', other: true }], files2 = [{ name: 'Lorem', other: true }, { name: 'Xxxxx', other: true }], result = function (array) { var o = {}, r = []; array.forEach(function (a) { if (!(a.name in o)) { o[a.name] = a; r.push(o[a.name]); } }); return r; }(files1.concat(files2)); document.write('<pre>' + JSON.stringify(result, 0, 4) + '</pre>'); 

You need to apply uniq method after union call.

The problem is that uniq by default uses === operator to test array element equality. So if you have array of objects, each object will be compared by reference , not value, which is obviously undesired behaviour. This problem can be solved by supplying uniq with extra parameter - callback function ( iteratee ) which will convert array element to simply comparable value. In your case you can use JSON.stringify method which returns string representation of an object.

function getAllFiles(alldocs) {
    var union = _.union.apply(null, alldocs.map(function(v) {
        return v.files;
    }));
    return _.uniq(union, function(v) {
        return JSON.stringify(v);
    });
}
var alldocs = [
    {files: [
       {name: 'Lorem', other: true},
       {name: 'Foo', other: true},
       {name: 'Bar', other: true}
    ]},
    {files: [
       {name: 'Lorem', other: true},
       {name: 'Xxxxx', other: true}
    ]},
    {files: [
       {name: 'Lorem', other: true},
       {name: 'Foo', other: true},
       {name: 'Epic', other: true},
       {name: 'Xxxxx', other: true}
    ]}
];
var result = getAllFiles(alldocs);

Fiddle

Using lodash, you can use _.uniqBy

result = _.uniqBy(result, function (e) {
  return e.name;
});

https://jsfiddle.net/mw230kea/1/

这是因为您使用的是对象数组,而下划线在比较项目 (例如名称,其他) 时不知道要使用哪个对象属性 ,这是一个干净的解决方案:

   files =  _.chain(files).indexBy("name").values();

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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