简体   繁体   中英

Getting unique objects from array of objects

I have an array of objects that looks like this:

[
  {
    "places": "NEW YORK",
    "locations": "USA"
  },
  {
    "places": "MONTREAL",
    "locations": "QUEBEC",
    "country": "Canada"
  },
  {
    "places": "MONTREAL",
    "locations": "QUEBEC",
    "country": "Canada"
  }
]

I want to filter and get only the unique entries. I tried the following function, but it doesn´t work:

function onlyUnique(value, index, self) { 
  return self.indexOf(value) === index;
}

Using a Set didn't seem to work either. Any ideas?

A simple solution using new Set() .

 const data = [ { "places": "NEW YORK", "locations": "USA" }, { "places": "MONTREAL", "locations": "QUEBEC", "country": "Canada" }, { "places": "MONTREAL", "locations": "QUEBEC", "country": "Canada" }, ]; const uniqueArray = a => [...new Set(a.map(o => JSON.stringify(o)))].map(s => JSON.parse(s)); console.log(uniqueArray(data)); 

You can do it like this

let data = data.filter(function (a) {
    let key = a.places + '|' + a.country;
    if (!this[key]) {
        this[key] = true;
        return true;
    }
}, Object.create(null));

console.log(data);

if you are using underscore, you can some thing like this :

 var list = [ { "places": "NEW YORK", "locations": "USA" }, { "places": "MONTREAL", "locations": "QUEBEC", "country": "Canada" }, { "places": "MONTREAL", "locations": "QUEBEC", "country": "Canada" }, ]; var uniqList = _.uniq(list, function (item) { return item.places+'_'+item.locations+'_'+item.country; }); console.log(uniqList); 
 <script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore.js"></script> 

Solution:

You can use filter with a local Set object. By comparing the stringified objects and the size property before and after trying to add to the Set , you can return a unique array by asking if the two numbers are not equal.


Code:

place_array.filter(v => b.size != b.add(JSON.stringify(v)).size, b = new Set());

Why use this code?

Unlike many of the other answers it relies on a singular pass through the array using the filter iterator. This means that it's more performant than multiple loops through an array. It also uses only localized variables, meaning that their is no global clutter and, since the filter method is functional, absolutely no side effects.

Note: There is always a hit to performance when using a Set Object in any situation. This comes down to Browser internals, however, when finding unique Objects/Arrays the stringify into a Set method is likely the best solution. Though with incredibly small data-sets you may find it practical to use an Array or build your own task-specific Object.

Working Example:

 let place_array = [{ "places": "NEW YORK", "locations": "USA" }, { "places": "MONTREAL", "locations": "QUEBEC", "country": "Canada" }, { "places": "MONTREAL", "locations": "QUEBEC", "country": "Canada" }, ]; let result = place_array.filter(v => b.size != b.add(JSON.stringify(v)).size, b = new Set()); console.log(result); 



Alternative Solution:

You can use filter with a local Array object. By determining whether or not the stringified objects are in the Array already and pushing if they're not, you can return a unique array by asking if the stringified object is not included in the array.

Code:

place_array.filter(v => !b.includes(s(v)) && !!b.push(s(v)), s = JSON.stringify, b = []);

Why use this code?

Effective though the Set object may be, it is ultimately slower due to backend processes than utilizing an Array object. You can prove this quite easily by running a test like a jsperf: https://jsperf.com/set-vs-ray-includes-battle/1

The Results of a Test:

测试结果证明阵列比设定速度更快

Working Example:

 let place_array = [{ "places": "NEW YORK", "locations": "USA" }, { "places": "MONTREAL", "locations": "QUEBEC", "country": "Canada" }, { "places": "MONTREAL", "locations": "QUEBEC", "country": "Canada" }, ]; let result = place_array.filter(v => !b.includes(s(v)) && !!b.push(s(v)), s = JSON.stringify, b = []); console.log(result); 

Something like this:

 var distObj = {};
 for(let i = 0; i < obj.places.lenght; i++){
    let tmp = obj.places;
    distObj[i].places = obj[i].places.filter(item => item !== tmp);
 }

If you combine this with for( k in Obj) and callback you can iterate over every deep and attr in Object

If your key values are simple as in your example, then you could do something like the following to handle instances where objects in your array have the same keys and values but not in the same order (since object property order is not guaranteed across browsers ).

  • Convert objects to arrays using Object.entries , sort, and convert to strings with JSON.stringify
  • Remove duplicates with Set and convert strings back to arrays using JSON.parse and then to objects using reduce to convert array pairs to object key value pairs

 const arr = [{ "places": "NEW YORK", "locations": "USA" }, { "places": "MONTREAL", "locations": "QUEBEC", "country": "Canada" }, { "country": "Canada", "places": "MONTREAL", "locations": "QUEBEC" } ]; let strings = arr.map(obj => JSON.stringify(Object.entries(obj).sort())); let result = [...new Set(strings)].map((s) => { return JSON.parse(s).reduce((acc, [k, v]) => { acc[k] = v; return acc; }, {}); }); console.log(result); // [{"locations":"USA","places":"NEW YORK"},{"country":"Canada","locations":"QUEBEC","places":"MONTREAL"}] 

You can try this.

let uniqueObjects= [];
objects.map(item => {
    let exists = uniqueProperties.some((object)=>{
        // Do check here
        return object.type == item.type;
    })
    if (!exists) {
        uniqueObjects.push(item);
    }
})

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