user10601452
user10601452

Reputation:

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?

Upvotes: 2

Views: 614

Answers (7)

benvc
benvc

Reputation: 15130

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" }];
const strings = arr.map((obj) => JSON.stringify(Object.entries(obj).sort()));
const 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"}]

Upvotes: 0

Shakil Alam
Shakil Alam

Reputation: 445

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);
    }
})

Upvotes: 0

zfrisch
zfrisch

Reputation: 8670

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:

Test Results Proving Array is Faster than Set

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);

Upvotes: 0

Aleksandar Stajic
Aleksandar Stajic

Reputation: 82

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

Upvotes: 0

Abdelkarim EL AMEL
Abdelkarim EL AMEL

Reputation: 1533

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>

Upvotes: 0

Damien
Damien

Reputation: 1620

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)); 

Upvotes: 3

Sergo Kupreishvili
Sergo Kupreishvili

Reputation: 462

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);

Upvotes: 0

Related Questions