Reputation:
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
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).
Object.entries
, sort, and convert to strings with JSON.stringify
Set
and convert strings back to arrays using JSON.parse
and then to objects using reduce
to convert array pairs to object key value pairsconst 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
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
Reputation: 8670
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.
place_array.filter(v => b.size != b.add(JSON.stringify(v)).size, b = new Set());
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.
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);
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.
place_array.filter(v => !b.includes(s(v)) && !!b.push(s(v)), s = JSON.stringify, b = []);
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
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
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
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
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
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