Related
I have an object that contains an array of objects.
obj = {};
obj.arr = new Array();
obj.arr.push({place:"here",name:"stuff"});
obj.arr.push({place:"there",name:"morestuff"});
obj.arr.push({place:"there",name:"morestuff"});
I'm wondering what is the best method to remove duplicate objects from an array. So for example, obj.arr would become...
{place:"here",name:"stuff"},
{place:"there",name:"morestuff"}
How about with some es6 magic?
obj.arr = obj.arr.filter((value, index, self) =>
index === self.findIndex((t) => (
t.place === value.place && t.name === value.name
))
)
Reference URL
A more generic solution would be:
const uniqueArray = obj.arr.filter((value, index) => {
const _value = JSON.stringify(value);
return index === obj.arr.findIndex(obj => {
return JSON.stringify(obj) === _value;
});
});
Using the above property strategy instead of JSON.stringify:
const isPropValuesEqual = (subject, target, propNames) =>
propNames.every(propName => subject[propName] === target[propName]);
const getUniqueItemsByProperties = (items, propNames) =>
items.filter((item, index, array) =>
index === array.findIndex(foundItem => isPropValuesEqual(foundItem, item, propNames))
);
You can add a wrapper if you want the propNames property to be either an array or a value:
const getUniqueItemsByProperties = (items, propNames) => {
const propNamesArray = Array.from(propNames);
return items.filter((item, index, array) =>
index === array.findIndex(foundItem => isPropValuesEqual(foundItem, item, propNamesArray))
);
};
allowing both getUniqueItemsByProperties('a') and getUniqueItemsByProperties(['a']);
Stackblitz Example
Explanation
Start by understanding the two methods used:
filter, findIndex
Next take your idea of what makes your two objects equal and keep that in mind.
We can detect something as a duplicate, if it satisfies the criterion that we have just thought of, but it's position is not at the first instance of an object with the criterion.
Therefore we can use the above criterion to determine if something is a duplicate.
One liners with filter ( Preserves order )
Find unique id's in an array.
arr.filter((v,i,a)=>a.findIndex(v2=>(v2.id===v.id))===i)
If the order is not important, map solutions will be faster: Solution with map
Unique by multiple properties ( place and name )
arr.filter((v,i,a)=>a.findIndex(v2=>['place','name'].every(k=>v2[k] ===v[k]))===i)
Unique by all properties (This will be slow for large arrays)
arr.filter((v,i,a)=>a.findIndex(v2=>(JSON.stringify(v2) === JSON.stringify(v)))===i)
Keep the last occurrence by replacing findIndex with findLastIndex.
arr.filter((v,i,a)=>a.findLastIndex(v2=>(v2.place === v.place))===i)
Using ES6+ in a single line you can get a unique list of objects by key:
const key = 'place';
const unique = [...new Map(arr.map(item => [item[key], item])).values()]
It can be put into a function:
function getUniqueListBy(arr, key) {
return [...new Map(arr.map(item => [item[key], item])).values()]
}
Here is a working example:
const arr = [
{place: "here", name: "x", other: "other stuff1" },
{place: "there", name: "x", other: "other stuff2" },
{place: "here", name: "y", other: "other stuff4" },
{place: "here", name: "z", other: "other stuff5" }
]
function getUniqueListBy(arr, key) {
return [...new Map(arr.map(item => [item[key], item])).values()]
}
const arr1 = getUniqueListBy(arr, 'place')
console.log("Unique by place")
console.log(JSON.stringify(arr1))
console.log("\nUnique by name")
const arr2 = getUniqueListBy(arr, 'name')
console.log(JSON.stringify(arr2))
How does it work
First the array is remapped in a way that it can be used as an input for a Map.
arr.map(item => [item[key], item]);
which means each item of the array will be transformed in another array with 2 elements; the selected key as first element and the entire initial item as second element, this is called an entry (ex. array entries, map entries). And here is the official doc with an example showing how to add array entries in Map constructor.
Example when key is place:
[["here", {place: "here", name: "x", other: "other stuff1" }], ...]
Secondly, we pass this modified array to the Map constructor and here is the magic happening. Map will eliminate the duplicate keys values, keeping only last inserted value of the same key.
Note: Map keeps the order of insertion. (check difference between Map and object)
new Map(entry array just mapped above)
Third we use the map values to retrieve the original items, but this time without duplicates.
new Map(mappedArr).values()
And last one is to add those values into a fresh new array so that it can look as the initial structure and return that:
return [...new Map(mappedArr).values()]
Simple and performant solution with a better runtime than the 70+ answers that already exist:
const ids = array.map(o => o.id)
const filtered = array.filter(({id}, index) => !ids.includes(id, index + 1))
Example:
const arr = [{id: 1, name: 'one'}, {id: 2, name: 'two'}, {id: 1, name: 'one'}]
const ids = arr.map(o => o.id)
const filtered = arr.filter(({id}, index) => !ids.includes(id, index + 1))
console.log(filtered)
How it works:
Array.filter() removes all duplicate objects by checking if the previously mapped id-array includes the current id ({id} destructs the object into only its id). To only filter out actual duplicates, it is using Array.includes()'s second parameter fromIndex with index + 1 which will ignore the current object and all previous.
Since every iteration of the filter callback method will only search the array beginning at the current index + 1, this also dramatically reduces the runtime because only objects not previously filtered get checked.
This obviously also works for any other key that is not called id, multiple or even all keys.
A primitive method would be:
const obj = {};
for (let i = 0, len = things.thing.length; i < len; i++) {
obj[things.thing[i]['place']] = things.thing[i];
}
things.thing = new Array();
for (const key in obj) {
things.thing.push(obj[key]);
}
If you can use Javascript libraries such as underscore or lodash, I recommend having a look at _.uniq function in their libraries. From lodash:
_.uniq(array, [isSorted=false], [callback=_.identity], [thisArg])
Basically, you pass in the array that in here is an object literal and you pass in the attribute that you want to remove duplicates with in the original data array, like this:
var data = [{'name': 'Amir', 'surname': 'Rahnama'}, {'name': 'Amir', 'surname': 'Stevens'}];
var non_duplidated_data = _.uniq(data, 'name');
UPDATE: Lodash now has introduced a .uniqBy as well.
I had this exact same requirement, to remove duplicate objects in a array, based on duplicates on a single field. I found the code here: Javascript: Remove Duplicates from Array of Objects
So in my example, I'm removing any object from the array that has a duplicate licenseNum string value.
var arrayWithDuplicates = [
{"type":"LICENSE", "licenseNum": "12345", state:"NV"},
{"type":"LICENSE", "licenseNum": "A7846", state:"CA"},
{"type":"LICENSE", "licenseNum": "12345", state:"OR"},
{"type":"LICENSE", "licenseNum": "10849", state:"CA"},
{"type":"LICENSE", "licenseNum": "B7037", state:"WA"},
{"type":"LICENSE", "licenseNum": "12345", state:"NM"}
];
function removeDuplicates(originalArray, prop) {
var newArray = [];
var lookupObject = {};
for(var i in originalArray) {
lookupObject[originalArray[i][prop]] = originalArray[i];
}
for(i in lookupObject) {
newArray.push(lookupObject[i]);
}
return newArray;
}
var uniqueArray = removeDuplicates(arrayWithDuplicates, "licenseNum");
console.log("uniqueArray is: " + JSON.stringify(uniqueArray));
The results:
uniqueArray is:
[{"type":"LICENSE","licenseNum":"10849","state":"CA"},
{"type":"LICENSE","licenseNum":"12345","state":"NM"},
{"type":"LICENSE","licenseNum":"A7846","state":"CA"},
{"type":"LICENSE","licenseNum":"B7037","state":"WA"}]
One liner using Set
var things = new Object();
things.thing = new Array();
things.thing.push({place:"here",name:"stuff"});
things.thing.push({place:"there",name:"morestuff"});
things.thing.push({place:"there",name:"morestuff"});
// assign things.thing to myData for brevity
var myData = things.thing;
things.thing = Array.from(new Set(myData.map(JSON.stringify))).map(JSON.parse);
console.log(things.thing)
Explanation:
new Set(myData.map(JSON.stringify)) creates a Set object using the stringified myData elements.
Set object will ensure that every element is unique.
Then I create an array based on the elements of the created set using Array.from.
Finally, I use JSON.parse to convert stringified element back to an object.
ES6 one liner is here
let arr = [
{id:1,name:"sravan ganji"},
{id:2,name:"pinky"},
{id:4,name:"mammu"},
{id:3,name:"avy"},
{id:3,name:"rashni"},
];
console.log(Object.values(arr.reduce((acc,cur)=>Object.assign(acc,{[cur.id]:cur}),{})))
To remove all duplicates from an array of objects, the simplest way is use filter:
var uniq = {};
var arr = [{"id":"1"},{"id":"1"},{"id":"2"}];
var arrFiltered = arr.filter(obj => !uniq[obj.id] && (uniq[obj.id] = true));
console.log('arrFiltered', arrFiltered);
One liners with Map ( High performance, Does not preserve order )
Find unique id's in array arr.
const arrUniq = [...new Map(arr.map(v => [v.id, v])).values()]
If the order is important check out the solution with filter: Solution with filter
Unique by multiple properties ( place and name ) in array arr
const arrUniq = [...new Map(arr.map(v => [JSON.stringify([v.place,v.name]), v])).values()]
Unique by all properties in array arr
const arrUniq = [...new Map(arr.map(v => [JSON.stringify(v), v])).values()]
Keep the first occurrence in array arr
const arrUniq = [...new Map(arr.slice().reverse().map(v => [v.id, v])).values()].reverse()
Here's another option to do it using Array iterating methods if you need comparison only by one field of an object:
function uniq(a, param){
return a.filter(function(item, pos, array){
return array.map(function(mapItem){ return mapItem[param]; }).indexOf(item[param]) === pos;
})
}
uniq(things.thing, 'place');
This is a generic way of doing this: you pass in a function that tests whether two elements of an array are considered equal. In this case, it compares the values of the name and place properties of the two objects being compared.
ES5 answer
function removeDuplicates(arr, equals) {
var originalArr = arr.slice(0);
var i, len, val;
arr.length = 0;
for (i = 0, len = originalArr.length; i < len; ++i) {
val = originalArr[i];
if (!arr.some(function(item) { return equals(item, val); })) {
arr.push(val);
}
}
}
function thingsEqual(thing1, thing2) {
return thing1.place === thing2.place
&& thing1.name === thing2.name;
}
var things = [
{place:"here",name:"stuff"},
{place:"there",name:"morestuff"},
{place:"there",name:"morestuff"}
];
removeDuplicates(things, thingsEqual);
console.log(things);
Original ES3 answer
function arrayContains(arr, val, equals) {
var i = arr.length;
while (i--) {
if ( equals(arr[i], val) ) {
return true;
}
}
return false;
}
function removeDuplicates(arr, equals) {
var originalArr = arr.slice(0);
var i, len, j, val;
arr.length = 0;
for (i = 0, len = originalArr.length; i < len; ++i) {
val = originalArr[i];
if (!arrayContains(arr, val, equals)) {
arr.push(val);
}
}
}
function thingsEqual(thing1, thing2) {
return thing1.place === thing2.place
&& thing1.name === thing2.name;
}
removeDuplicates(things.thing, thingsEqual);
If you can wait to eliminate the duplicates until after all the additions, the typical approach is to first sort the array and then eliminate duplicates. The sorting avoids the N * N approach of scanning the array for each element as you walk through them.
The "eliminate duplicates" function is usually called unique or uniq. Some existing implementations may combine the two steps, e.g., prototype's uniq
This post has few ideas to try (and some to avoid :-) ) if your library doesn't already have one! Personally I find this one the most straight forward:
function unique(a){
a.sort();
for(var i = 1; i < a.length; ){
if(a[i-1] == a[i]){
a.splice(i, 1);
} else {
i++;
}
}
return a;
}
// Provide your own comparison
function unique(a, compareFunc){
a.sort( compareFunc );
for(var i = 1; i < a.length; ){
if( compareFunc(a[i-1], a[i]) === 0){
a.splice(i, 1);
} else {
i++;
}
}
return a;
}
I think the best approach is using reduce and Map object. This is a single line solution.
const data = [
{id: 1, name: 'David'},
{id: 2, name: 'Mark'},
{id: 2, name: 'Lora'},
{id: 4, name: 'Tyler'},
{id: 4, name: 'Donald'},
{id: 5, name: 'Adrian'},
{id: 6, name: 'Michael'}
]
const uniqueData = [...data.reduce((map, obj) => map.set(obj.id, obj), new Map()).values()];
console.log(uniqueData)
/*
in `map.set(obj.id, obj)`
'obj.id' is key. (don't worry. we'll get only values using the .values() method)
'obj' is whole object.
*/
To add one more to the list. Using ES6 and Array.reduce with Array.find.
In this example filtering objects based on a guid property.
let filtered = array.reduce((accumulator, current) => {
if (! accumulator.find(({guid}) => guid === current.guid)) {
accumulator.push(current);
}
return accumulator;
}, []);
Extending this one to allow selection of a property and compress it into a one liner:
const uniqify = (array, key) => array.reduce((prev, curr) => prev.find(a => a[key] === curr[key]) ? prev : prev.push(curr) && prev, []);
To use it pass an array of objects and the name of the key you wish to de-dupe on as a string value:
const result = uniqify(myArrayOfObjects, 'guid')
Considering lodash.uniqWith
const objects = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }, { 'x': 1, 'y': 2 }];
_.uniqWith(objects, _.isEqual);
// => [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }]
You could also use a Map:
const dedupThings = Array.from(things.thing.reduce((m, t) => m.set(t.place, t), new Map()).values());
Full sample:
const things = new Object();
things.thing = new Array();
things.thing.push({place:"here",name:"stuff"});
things.thing.push({place:"there",name:"morestuff"});
things.thing.push({place:"there",name:"morestuff"});
const dedupThings = Array.from(things.thing.reduce((m, t) => m.set(t.place, t), new Map()).values());
console.log(JSON.stringify(dedupThings, null, 4));
Result:
[
{
"place": "here",
"name": "stuff"
},
{
"place": "there",
"name": "morestuff"
}
]
Dang, kids, let's crush this thing down, why don't we?
let uniqIds = {}, source = [{id:'a'},{id:'b'},{id:'c'},{id:'b'},{id:'a'},{id:'d'}];
let filtered = source.filter(obj => !uniqIds[obj.id] && (uniqIds[obj.id] = true));
console.log(filtered);
// EXPECTED: [{id:'a'},{id:'b'},{id:'c'},{id:'d'}];
let myData = [{place:"here",name:"stuff"},
{place:"there",name:"morestuff"},
{place:"there",name:"morestuff"}];
let q = [...new Map(myData.map(obj => [JSON.stringify(obj), obj])).values()];
console.log(q)
One-liner using ES6 and new Map().
// assign things.thing to myData
let myData = things.thing;
[...new Map(myData.map(obj => [JSON.stringify(obj), obj])).values()];
Details:-
Doing .map() on the data list and converting each individual object into a [key, value] pair array(length =2), the first element(key) would be the stringified version of the object and second(value) would be an object itself.
Adding above created array list to new Map() would have the key as stringified object and any same key addition would result in overriding the already existing key.
Using .values() would give MapIterator with all values in a Map (obj in our case)
Finally, spread ... operator to give new Array with values from the above step.
A TypeScript solution
This will remove duplicate objects and also preserve the types of the objects.
function removeDuplicateObjects(array: any[]) {
return [...new Set(array.map(s => JSON.stringify(s)))]
.map(s => JSON.parse(s));
}
const things = [
{place:"here",name:"stuff"},
{place:"there",name:"morestuff"},
{place:"there",name:"morestuff"}
];
const filteredArr = things.reduce((thing, current) => {
const x = thing.find(item => item.place === current.place);
if (!x) {
return thing.concat([current]);
} else {
return thing;
}
}, []);
console.log(filteredArr)
Solution Via Set Object | According to the data type
const seen = new Set();
const things = [
{place:"here",name:"stuff"},
{place:"there",name:"morestuff"},
{place:"there",name:"morestuff"}
];
const filteredArr = things.filter(el => {
const duplicate = seen.has(el.place);
seen.add(el.place);
return !duplicate;
});
console.log(filteredArr)
Set Object Feature
Each value in the Set Object has to be unique, the value equality will be checked
The Purpose of Set object storing unique values according to the Data type , whether primitive values or object references.it has very useful four Instance methods add, clear , has & delete.
Unique & data Type feature:..
addmethod
it's push unique data into collection by default also preserve data type .. that means it prevent to push duplicate item into collection also it will check data type by default...
has method
sometime needs to check data item exist into the collection and . it's handy method for the collection to cheek unique id or item and data type..
delete method
it will remove specific item from the collection by identifying data type..
clear method
it will remove all collection items from one specific variable and set as empty object
Set object has also Iteration methods & more feature..
Better Read from Here : Set - JavaScript | MDN
removeDuplicates() takes in an array of objects and returns a new array without any duplicate objects (based on the id property).
const allTests = [
{name: 'Test1', id: '1'},
{name: 'Test3', id: '3'},
{name: 'Test2', id: '2'},
{name: 'Test2', id: '2'},
{name: 'Test3', id: '3'}
];
function removeDuplicates(array) {
let uniq = {};
return array.filter(obj => !uniq[obj.id] && (uniq[obj.id] = true))
}
removeDuplicates(allTests);
Expected outcome:
[
{name: 'Test1', id: '1'},
{name: 'Test3', id: '3'},
{name: 'Test2', id: '2'}
];
First, we set the value of variable uniq to an empty object.
Next, we filter through the array of objects. Filter creates a new array with all elements that pass the test implemented by the provided function.
return array.filter(obj => !uniq[obj.id] && (uniq[obj.id] = true));
Above, we use the short-circuiting functionality of &&. If the left side of the && evaluates to true, then it returns the value on the right of the &&. If the left side is false, it returns what is on the left side of the &&.
For each object(obj) we check uniq for a property named the value of obj.id (In this case, on the first iteration it would check for the property '1'.) We want the opposite of what it returns (either true or false) which is why we use the ! in !uniq[obj.id]. If uniq has the id property already, it returns true which evaluates to false (!) telling the filter function NOT to add that obj. However, if it does not find the obj.id property, it returns false which then evaluates to true (!) and returns everything to the right of the &&, or (uniq[obj.id] = true). This is a truthy value, telling the filter method to add that obj to the returned array, and it also adds the property {1: true} to uniq. This ensures that any other obj instance with that same id will not be added again.
Fast (less runtime) and type-safe answer for lazy Typescript developers:
export const uniqueBy = <T>( uniqueKey: keyof T, objects: T[]): T[] => {
const ids = objects.map(object => object[uniqueKey]);
return objects.filter((object, index) => !ids.includes(object[uniqueKey], index + 1));
}
This way works well for me:
function arrayUnique(arr, uniqueKey) {
const flagList = new Set()
return arr.filter(function(item) {
if (!flagList.has(item[uniqueKey])) {
flagList.add(item[uniqueKey])
return true
}
})
}
const data = [
{
name: 'Kyle',
occupation: 'Fashion Designer'
},
{
name: 'Kyle',
occupation: 'Fashion Designer'
},
{
name: 'Emily',
occupation: 'Web Designer'
},
{
name: 'Melissa',
occupation: 'Fashion Designer'
},
{
name: 'Tom',
occupation: 'Web Developer'
},
{
name: 'Tom',
occupation: 'Web Developer'
}
]
console.table(arrayUnique(data, 'name'))// work well
printout
┌─────────┬───────────┬────────────────────┐
│ (index) │ name │ occupation │
├─────────┼───────────┼────────────────────┤
│ 0 │ 'Kyle' │ 'Fashion Designer' │
│ 1 │ 'Emily' │ 'Web Designer' │
│ 2 │ 'Melissa' │ 'Fashion Designer' │
│ 3 │ 'Tom' │ 'Web Developer' │
└─────────┴───────────┴────────────────────┘
ES5:
function arrayUnique(arr, uniqueKey) {
const flagList = []
return arr.filter(function(item) {
if (flagList.indexOf(item[uniqueKey]) === -1) {
flagList.push(item[uniqueKey])
return true
}
})
}
These two ways are simpler and more understandable.
Here is a solution for ES6 where you only want to keep the last item. This solution is functional and Airbnb style compliant.
const things = {
thing: [
{ place: 'here', name: 'stuff' },
{ place: 'there', name: 'morestuff1' },
{ place: 'there', name: 'morestuff2' },
],
};
const removeDuplicates = (array, key) => {
return array.reduce((arr, item) => {
const removed = arr.filter(i => i[key] !== item[key]);
return [...removed, item];
}, []);
};
console.log(removeDuplicates(things.thing, 'place'));
// > [{ place: 'here', name: 'stuff' }, { place: 'there', name: 'morestuff2' }]
I know there is a ton of answers in this question already, but bear with me...
Some of the objects in your array may have additional properties that you are not interested in, or you simply want to find the unique objects considering only a subset of the properties.
Consider the array below. Say you want to find the unique objects in this array considering only propOne and propTwo, and ignore any other properties that may be there.
The expected result should include only the first and last objects. So here goes the code:
const array = [{
propOne: 'a',
propTwo: 'b',
propThree: 'I have no part in this...'
},
{
propOne: 'a',
propTwo: 'b',
someOtherProperty: 'no one cares about this...'
},
{
propOne: 'x',
propTwo: 'y',
yetAnotherJunk: 'I am valueless really',
noOneHasThis: 'I have something no one has'
}];
const uniques = [...new Set(
array.map(x => JSON.stringify(((o) => ({
propOne: o.propOne,
propTwo: o.propTwo
}))(x))))
].map(JSON.parse);
console.log(uniques);
Another option would be to create a custom indexOf function, which compares the values of your chosen property for each object and wrap this in a reduce function.
var uniq = redundant_array.reduce(function(a,b){
function indexOfProperty (a, b){
for (var i=0;i<a.length;i++){
if(a[i].property == b.property){
return i;
}
}
return -1;
}
if (indexOfProperty(a,b) < 0 ) a.push(b);
return a;
},[]);
Here I found a simple solution for removing duplicates from an array of objects using reduce method. I am filtering elements based on the position key of an object
const med = [
{name: 'name1', position: 'left'},
{name: 'name2', position: 'right'},
{name: 'name3', position: 'left'},
{name: 'name4', position: 'right'},
{name: 'name5', position: 'left'},
{name: 'name6', position: 'left1'}
]
const arr = [];
med.reduce((acc, curr) => {
if(acc.indexOf(curr.position) === -1) {
acc.push(curr.position);
arr.push(curr);
}
return acc;
}, [])
console.log(arr)
If array contains objects, then you can use this to remove duplicate
const persons= [
{ id: 1, name: 'John',phone:'23' },
{ id: 2, name: 'Jane',phone:'23'},
{ id: 1, name: 'Johnny',phone:'56' },
{ id: 4, name: 'Alice',phone:'67' },
];
const unique = [...new Map(persons.map((m) => [m.id, m])).values()];
if remove duplicates on the basis of phone, just replace m.id with m.phone
const unique = [...new Map(persons.map((m) => [m.phone, m])).values()];
friends. I'm trying to write code that hashes all values in a JSON file, regardless of file structure, while preserving the keys and structure. I'm new to javascript, and am having some trouble. My code hashes the values of big and baz, but doesn't recursively hash the values of cat and bar like I want it to. Ideally, I want the numbers hashed and the names (big, foo, etc.) left alone. Thank you so much! See my code below:
var meow = {
big: 20,
baz: {
foo: {
cat: 3,
bar: 5
}
}
};
console.log(typeof(meow.baz.foo));
function hashobj(obj)
{
var valarray = Object.keys(obj);
var zer = valarray[0];
for(var i = 0; i < valarray.length; i++)
{
var vaz = valarray[i];
if(typeof(obj[vaz] != "object"))
{
obj[vaz] = sha256(obj[vaz] + buf);
}
if(typeof(obj[vaz]) == "object")
{
console.log("HERE");
hashobj(obj[vaz]);
}
}
}
hashobj(meow);
console.log(meow);
If you're looking to do this recursively, I would suggest using a generic transformation function that handles the recursive object structure and delegates to a supplied function the actual work of transforming the leaf nodes.
In this version, the transform function does all the heavy lifting. It calls the supplied function on scalar values and recursively calls itself on objects and arrays, recreating the structure of the original with the new values. This is quite reusable.
We create our hashObject function by passing transform a function which does the sha256 encoding of our values.
const transform = (fn) => (obj) =>
Array.isArray (obj)
? obj .map (transform (fn))
: Object (obj) === obj
? Object .fromEntries (Object .entries (obj)
.map (([k, v]) => [k, transform (fn) (v)])
)
: fn (obj)
const hashObj = transform ((n) => sha256 (String (n)))
const meow = {big: 20, baz: {foo: {cat: 3, bar: 5, qux: [1, 2, 3]}}};
// added to demonstrate arrays --------^
console .log (hashObj (meow))
.as-console-wrapper {max-height: 100% !important; top: 0}
<script src="https://unpkg.com/js-sha256#0.9.0/src/sha256.js"></script>
Scott's answer is wonderful. The optional chaining operator, ?., is supported most places now and is particularly useful for runtime type checking. I'm sharing this post as a way to see transform expressed using this modern feature -
function transform (f, o)
{ switch (o?.constructor) // <- any o, even null and undefined
{ case Array:
return o.map(_ => transform(f, _))
case Object:
return Object.fromEntries
( Object
.entries(o)
.map(([k, _]) => [k, transform(f, _)])
)
default:
return f(o)
}
}
const result =
transform
( _ => sha256(String(_))
, {big: 20, baz: {foo: {cat: 3, bar: 5, qux: [1, 2, 3]}}}
)
console.log(result)
.as-console-wrapper {max-height: 100% !important; top: 0}
<script src="https://unpkg.com/js-sha256#0.9.0/src/sha256.js"></script>
{
"big": "f5ca38f748a1d6eaf726b8a42fb575c3c71f1864a8143301782de13da2d9202b",
"baz": {
"foo": {
"cat": "4e07408562bedb8b60ce05c1decfe3ad16b72230967de01f640b7e4729b49fce",
"bar": "ef2d127de37b942baad06145e54b0c619a1f22327b2ebbcfbec78f5564afe39d",
"qux": [
"6b86b273ff34fce19d6b804eff5a3f5747ada4eaa22f1d49c01e52ddb7875b4b",
"d4735e3a265e16eee03f59718b9b5d03019c07d8b6c51f90da3a666eec13ab35",
"4e07408562bedb8b60ce05c1decfe3ad16b72230967de01f640b7e4729b49fce"
]
}
}
}
One distinct advantage to this approach is the Array and Object branches can appear in any order. When using Array.isArray(t) it must be checked before checking Object(t) === t. It's a subtle thing but is worth noting -
// also correct!
function transform (f, o)
{ switch (o?.constructor)
{ case Object: // <- type-check Object
return // ...
case Array: // <- type-check Array
return // ...
default:
return f(o)
}
}
You may also wish to hash an entire object. Here's one possibility to implement a generic hash using a generic traverse function -
function* traverse (t, r = [])
{ switch (t?.constructor) // <- any t, even null and undefined
{ case Array:
case Set:
case Map:
for (const [k, _] of t.entries())
yield* traverse(_, [...r, k])
break
case Object:
for (const [k, _] of Object.entries(t))
yield* traverse(_, [...r, k])
break
default:
yield [r, t]
}
}
function hash (t)
{ const r = sha256.create()
for (const [k, v] of traverse(t))
r.update(k.concat(v).join(":"))
return r.hex()
}
const input =
{big: 20, baz: {foo: {cat: 3, bar: 5, qux: [1, 2, 3]}}}
console.log(hash("foo"), hash("foo"))
console.log(hash([1,2,3]), hash([1,2,3]))
console.log(hash(input), hash(input))
.as-console-wrapper {max-height: 100% !important; top: 0}
<script src="https://unpkg.com/js-sha256#0.9.0/src/sha256.js"></script>
2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae
2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae
492f06976c8bc705819f5d33d71be6a80a547b03f87c377e3543605d8260159c
492f06976c8bc705819f5d33d71be6a80a547b03f87c377e3543605d8260159c
d1ae8b8641d3d6d65b1e4eecab0484a9f9618f2aabafe473c8bb0b4f6382695c
d1ae8b8641d3d6d65b1e4eecab0484a9f9618f2aabafe473c8bb0b4f6382695c
Everything is ok but the parenthesis:
if(typeof(obj[vaz] != "object"))
should read:
if(typeof(obj[vaz]) != "object")
I have an object that contains an array of objects.
obj = {};
obj.arr = new Array();
obj.arr.push({place:"here",name:"stuff"});
obj.arr.push({place:"there",name:"morestuff"});
obj.arr.push({place:"there",name:"morestuff"});
I'm wondering what is the best method to remove duplicate objects from an array. So for example, obj.arr would become...
{place:"here",name:"stuff"},
{place:"there",name:"morestuff"}
How about with some es6 magic?
obj.arr = obj.arr.filter((value, index, self) =>
index === self.findIndex((t) => (
t.place === value.place && t.name === value.name
))
)
Reference URL
A more generic solution would be:
const uniqueArray = obj.arr.filter((value, index) => {
const _value = JSON.stringify(value);
return index === obj.arr.findIndex(obj => {
return JSON.stringify(obj) === _value;
});
});
Using the above property strategy instead of JSON.stringify:
const isPropValuesEqual = (subject, target, propNames) =>
propNames.every(propName => subject[propName] === target[propName]);
const getUniqueItemsByProperties = (items, propNames) =>
items.filter((item, index, array) =>
index === array.findIndex(foundItem => isPropValuesEqual(foundItem, item, propNames))
);
You can add a wrapper if you want the propNames property to be either an array or a value:
const getUniqueItemsByProperties = (items, propNames) => {
const propNamesArray = Array.from(propNames);
return items.filter((item, index, array) =>
index === array.findIndex(foundItem => isPropValuesEqual(foundItem, item, propNamesArray))
);
};
allowing both getUniqueItemsByProperties('a') and getUniqueItemsByProperties(['a']);
Stackblitz Example
Explanation
Start by understanding the two methods used:
filter, findIndex
Next take your idea of what makes your two objects equal and keep that in mind.
We can detect something as a duplicate, if it satisfies the criterion that we have just thought of, but it's position is not at the first instance of an object with the criterion.
Therefore we can use the above criterion to determine if something is a duplicate.
One liners with filter ( Preserves order )
Find unique id's in an array.
arr.filter((v,i,a)=>a.findIndex(v2=>(v2.id===v.id))===i)
If the order is not important, map solutions will be faster: Solution with map
Unique by multiple properties ( place and name )
arr.filter((v,i,a)=>a.findIndex(v2=>['place','name'].every(k=>v2[k] ===v[k]))===i)
Unique by all properties (This will be slow for large arrays)
arr.filter((v,i,a)=>a.findIndex(v2=>(JSON.stringify(v2) === JSON.stringify(v)))===i)
Keep the last occurrence by replacing findIndex with findLastIndex.
arr.filter((v,i,a)=>a.findLastIndex(v2=>(v2.place === v.place))===i)
Using ES6+ in a single line you can get a unique list of objects by key:
const key = 'place';
const unique = [...new Map(arr.map(item => [item[key], item])).values()]
It can be put into a function:
function getUniqueListBy(arr, key) {
return [...new Map(arr.map(item => [item[key], item])).values()]
}
Here is a working example:
const arr = [
{place: "here", name: "x", other: "other stuff1" },
{place: "there", name: "x", other: "other stuff2" },
{place: "here", name: "y", other: "other stuff4" },
{place: "here", name: "z", other: "other stuff5" }
]
function getUniqueListBy(arr, key) {
return [...new Map(arr.map(item => [item[key], item])).values()]
}
const arr1 = getUniqueListBy(arr, 'place')
console.log("Unique by place")
console.log(JSON.stringify(arr1))
console.log("\nUnique by name")
const arr2 = getUniqueListBy(arr, 'name')
console.log(JSON.stringify(arr2))
How does it work
First the array is remapped in a way that it can be used as an input for a Map.
arr.map(item => [item[key], item]);
which means each item of the array will be transformed in another array with 2 elements; the selected key as first element and the entire initial item as second element, this is called an entry (ex. array entries, map entries). And here is the official doc with an example showing how to add array entries in Map constructor.
Example when key is place:
[["here", {place: "here", name: "x", other: "other stuff1" }], ...]
Secondly, we pass this modified array to the Map constructor and here is the magic happening. Map will eliminate the duplicate keys values, keeping only last inserted value of the same key.
Note: Map keeps the order of insertion. (check difference between Map and object)
new Map(entry array just mapped above)
Third we use the map values to retrieve the original items, but this time without duplicates.
new Map(mappedArr).values()
And last one is to add those values into a fresh new array so that it can look as the initial structure and return that:
return [...new Map(mappedArr).values()]
Simple and performant solution with a better runtime than the 70+ answers that already exist:
const ids = array.map(o => o.id)
const filtered = array.filter(({id}, index) => !ids.includes(id, index + 1))
Example:
const arr = [{id: 1, name: 'one'}, {id: 2, name: 'two'}, {id: 1, name: 'one'}]
const ids = arr.map(o => o.id)
const filtered = arr.filter(({id}, index) => !ids.includes(id, index + 1))
console.log(filtered)
How it works:
Array.filter() removes all duplicate objects by checking if the previously mapped id-array includes the current id ({id} destructs the object into only its id). To only filter out actual duplicates, it is using Array.includes()'s second parameter fromIndex with index + 1 which will ignore the current object and all previous.
Since every iteration of the filter callback method will only search the array beginning at the current index + 1, this also dramatically reduces the runtime because only objects not previously filtered get checked.
This obviously also works for any other key that is not called id, multiple or even all keys.
A primitive method would be:
const obj = {};
for (let i = 0, len = things.thing.length; i < len; i++) {
obj[things.thing[i]['place']] = things.thing[i];
}
things.thing = new Array();
for (const key in obj) {
things.thing.push(obj[key]);
}
If you can use Javascript libraries such as underscore or lodash, I recommend having a look at _.uniq function in their libraries. From lodash:
_.uniq(array, [isSorted=false], [callback=_.identity], [thisArg])
Basically, you pass in the array that in here is an object literal and you pass in the attribute that you want to remove duplicates with in the original data array, like this:
var data = [{'name': 'Amir', 'surname': 'Rahnama'}, {'name': 'Amir', 'surname': 'Stevens'}];
var non_duplidated_data = _.uniq(data, 'name');
UPDATE: Lodash now has introduced a .uniqBy as well.
I had this exact same requirement, to remove duplicate objects in a array, based on duplicates on a single field. I found the code here: Javascript: Remove Duplicates from Array of Objects
So in my example, I'm removing any object from the array that has a duplicate licenseNum string value.
var arrayWithDuplicates = [
{"type":"LICENSE", "licenseNum": "12345", state:"NV"},
{"type":"LICENSE", "licenseNum": "A7846", state:"CA"},
{"type":"LICENSE", "licenseNum": "12345", state:"OR"},
{"type":"LICENSE", "licenseNum": "10849", state:"CA"},
{"type":"LICENSE", "licenseNum": "B7037", state:"WA"},
{"type":"LICENSE", "licenseNum": "12345", state:"NM"}
];
function removeDuplicates(originalArray, prop) {
var newArray = [];
var lookupObject = {};
for(var i in originalArray) {
lookupObject[originalArray[i][prop]] = originalArray[i];
}
for(i in lookupObject) {
newArray.push(lookupObject[i]);
}
return newArray;
}
var uniqueArray = removeDuplicates(arrayWithDuplicates, "licenseNum");
console.log("uniqueArray is: " + JSON.stringify(uniqueArray));
The results:
uniqueArray is:
[{"type":"LICENSE","licenseNum":"10849","state":"CA"},
{"type":"LICENSE","licenseNum":"12345","state":"NM"},
{"type":"LICENSE","licenseNum":"A7846","state":"CA"},
{"type":"LICENSE","licenseNum":"B7037","state":"WA"}]
One liner using Set
var things = new Object();
things.thing = new Array();
things.thing.push({place:"here",name:"stuff"});
things.thing.push({place:"there",name:"morestuff"});
things.thing.push({place:"there",name:"morestuff"});
// assign things.thing to myData for brevity
var myData = things.thing;
things.thing = Array.from(new Set(myData.map(JSON.stringify))).map(JSON.parse);
console.log(things.thing)
Explanation:
new Set(myData.map(JSON.stringify)) creates a Set object using the stringified myData elements.
Set object will ensure that every element is unique.
Then I create an array based on the elements of the created set using Array.from.
Finally, I use JSON.parse to convert stringified element back to an object.
ES6 one liner is here
let arr = [
{id:1,name:"sravan ganji"},
{id:2,name:"pinky"},
{id:4,name:"mammu"},
{id:3,name:"avy"},
{id:3,name:"rashni"},
];
console.log(Object.values(arr.reduce((acc,cur)=>Object.assign(acc,{[cur.id]:cur}),{})))
To remove all duplicates from an array of objects, the simplest way is use filter:
var uniq = {};
var arr = [{"id":"1"},{"id":"1"},{"id":"2"}];
var arrFiltered = arr.filter(obj => !uniq[obj.id] && (uniq[obj.id] = true));
console.log('arrFiltered', arrFiltered);
One liners with Map ( High performance, Does not preserve order )
Find unique id's in array arr.
const arrUniq = [...new Map(arr.map(v => [v.id, v])).values()]
If the order is important check out the solution with filter: Solution with filter
Unique by multiple properties ( place and name ) in array arr
const arrUniq = [...new Map(arr.map(v => [JSON.stringify([v.place,v.name]), v])).values()]
Unique by all properties in array arr
const arrUniq = [...new Map(arr.map(v => [JSON.stringify(v), v])).values()]
Keep the first occurrence in array arr
const arrUniq = [...new Map(arr.slice().reverse().map(v => [v.id, v])).values()].reverse()
Here's another option to do it using Array iterating methods if you need comparison only by one field of an object:
function uniq(a, param){
return a.filter(function(item, pos, array){
return array.map(function(mapItem){ return mapItem[param]; }).indexOf(item[param]) === pos;
})
}
uniq(things.thing, 'place');
This is a generic way of doing this: you pass in a function that tests whether two elements of an array are considered equal. In this case, it compares the values of the name and place properties of the two objects being compared.
ES5 answer
function removeDuplicates(arr, equals) {
var originalArr = arr.slice(0);
var i, len, val;
arr.length = 0;
for (i = 0, len = originalArr.length; i < len; ++i) {
val = originalArr[i];
if (!arr.some(function(item) { return equals(item, val); })) {
arr.push(val);
}
}
}
function thingsEqual(thing1, thing2) {
return thing1.place === thing2.place
&& thing1.name === thing2.name;
}
var things = [
{place:"here",name:"stuff"},
{place:"there",name:"morestuff"},
{place:"there",name:"morestuff"}
];
removeDuplicates(things, thingsEqual);
console.log(things);
Original ES3 answer
function arrayContains(arr, val, equals) {
var i = arr.length;
while (i--) {
if ( equals(arr[i], val) ) {
return true;
}
}
return false;
}
function removeDuplicates(arr, equals) {
var originalArr = arr.slice(0);
var i, len, j, val;
arr.length = 0;
for (i = 0, len = originalArr.length; i < len; ++i) {
val = originalArr[i];
if (!arrayContains(arr, val, equals)) {
arr.push(val);
}
}
}
function thingsEqual(thing1, thing2) {
return thing1.place === thing2.place
&& thing1.name === thing2.name;
}
removeDuplicates(things.thing, thingsEqual);
If you can wait to eliminate the duplicates until after all the additions, the typical approach is to first sort the array and then eliminate duplicates. The sorting avoids the N * N approach of scanning the array for each element as you walk through them.
The "eliminate duplicates" function is usually called unique or uniq. Some existing implementations may combine the two steps, e.g., prototype's uniq
This post has few ideas to try (and some to avoid :-) ) if your library doesn't already have one! Personally I find this one the most straight forward:
function unique(a){
a.sort();
for(var i = 1; i < a.length; ){
if(a[i-1] == a[i]){
a.splice(i, 1);
} else {
i++;
}
}
return a;
}
// Provide your own comparison
function unique(a, compareFunc){
a.sort( compareFunc );
for(var i = 1; i < a.length; ){
if( compareFunc(a[i-1], a[i]) === 0){
a.splice(i, 1);
} else {
i++;
}
}
return a;
}
I think the best approach is using reduce and Map object. This is a single line solution.
const data = [
{id: 1, name: 'David'},
{id: 2, name: 'Mark'},
{id: 2, name: 'Lora'},
{id: 4, name: 'Tyler'},
{id: 4, name: 'Donald'},
{id: 5, name: 'Adrian'},
{id: 6, name: 'Michael'}
]
const uniqueData = [...data.reduce((map, obj) => map.set(obj.id, obj), new Map()).values()];
console.log(uniqueData)
/*
in `map.set(obj.id, obj)`
'obj.id' is key. (don't worry. we'll get only values using the .values() method)
'obj' is whole object.
*/
To add one more to the list. Using ES6 and Array.reduce with Array.find.
In this example filtering objects based on a guid property.
let filtered = array.reduce((accumulator, current) => {
if (! accumulator.find(({guid}) => guid === current.guid)) {
accumulator.push(current);
}
return accumulator;
}, []);
Extending this one to allow selection of a property and compress it into a one liner:
const uniqify = (array, key) => array.reduce((prev, curr) => prev.find(a => a[key] === curr[key]) ? prev : prev.push(curr) && prev, []);
To use it pass an array of objects and the name of the key you wish to de-dupe on as a string value:
const result = uniqify(myArrayOfObjects, 'guid')
Considering lodash.uniqWith
const objects = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }, { 'x': 1, 'y': 2 }];
_.uniqWith(objects, _.isEqual);
// => [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }]
You could also use a Map:
const dedupThings = Array.from(things.thing.reduce((m, t) => m.set(t.place, t), new Map()).values());
Full sample:
const things = new Object();
things.thing = new Array();
things.thing.push({place:"here",name:"stuff"});
things.thing.push({place:"there",name:"morestuff"});
things.thing.push({place:"there",name:"morestuff"});
const dedupThings = Array.from(things.thing.reduce((m, t) => m.set(t.place, t), new Map()).values());
console.log(JSON.stringify(dedupThings, null, 4));
Result:
[
{
"place": "here",
"name": "stuff"
},
{
"place": "there",
"name": "morestuff"
}
]
Dang, kids, let's crush this thing down, why don't we?
let uniqIds = {}, source = [{id:'a'},{id:'b'},{id:'c'},{id:'b'},{id:'a'},{id:'d'}];
let filtered = source.filter(obj => !uniqIds[obj.id] && (uniqIds[obj.id] = true));
console.log(filtered);
// EXPECTED: [{id:'a'},{id:'b'},{id:'c'},{id:'d'}];
let myData = [{place:"here",name:"stuff"},
{place:"there",name:"morestuff"},
{place:"there",name:"morestuff"}];
let q = [...new Map(myData.map(obj => [JSON.stringify(obj), obj])).values()];
console.log(q)
One-liner using ES6 and new Map().
// assign things.thing to myData
let myData = things.thing;
[...new Map(myData.map(obj => [JSON.stringify(obj), obj])).values()];
Details:-
Doing .map() on the data list and converting each individual object into a [key, value] pair array(length =2), the first element(key) would be the stringified version of the object and second(value) would be an object itself.
Adding above created array list to new Map() would have the key as stringified object and any same key addition would result in overriding the already existing key.
Using .values() would give MapIterator with all values in a Map (obj in our case)
Finally, spread ... operator to give new Array with values from the above step.
A TypeScript solution
This will remove duplicate objects and also preserve the types of the objects.
function removeDuplicateObjects(array: any[]) {
return [...new Set(array.map(s => JSON.stringify(s)))]
.map(s => JSON.parse(s));
}
const things = [
{place:"here",name:"stuff"},
{place:"there",name:"morestuff"},
{place:"there",name:"morestuff"}
];
const filteredArr = things.reduce((thing, current) => {
const x = thing.find(item => item.place === current.place);
if (!x) {
return thing.concat([current]);
} else {
return thing;
}
}, []);
console.log(filteredArr)
Solution Via Set Object | According to the data type
const seen = new Set();
const things = [
{place:"here",name:"stuff"},
{place:"there",name:"morestuff"},
{place:"there",name:"morestuff"}
];
const filteredArr = things.filter(el => {
const duplicate = seen.has(el.place);
seen.add(el.place);
return !duplicate;
});
console.log(filteredArr)
Set Object Feature
Each value in the Set Object has to be unique, the value equality will be checked
The Purpose of Set object storing unique values according to the Data type , whether primitive values or object references.it has very useful four Instance methods add, clear , has & delete.
Unique & data Type feature:..
addmethod
it's push unique data into collection by default also preserve data type .. that means it prevent to push duplicate item into collection also it will check data type by default...
has method
sometime needs to check data item exist into the collection and . it's handy method for the collection to cheek unique id or item and data type..
delete method
it will remove specific item from the collection by identifying data type..
clear method
it will remove all collection items from one specific variable and set as empty object
Set object has also Iteration methods & more feature..
Better Read from Here : Set - JavaScript | MDN
removeDuplicates() takes in an array of objects and returns a new array without any duplicate objects (based on the id property).
const allTests = [
{name: 'Test1', id: '1'},
{name: 'Test3', id: '3'},
{name: 'Test2', id: '2'},
{name: 'Test2', id: '2'},
{name: 'Test3', id: '3'}
];
function removeDuplicates(array) {
let uniq = {};
return array.filter(obj => !uniq[obj.id] && (uniq[obj.id] = true))
}
removeDuplicates(allTests);
Expected outcome:
[
{name: 'Test1', id: '1'},
{name: 'Test3', id: '3'},
{name: 'Test2', id: '2'}
];
First, we set the value of variable uniq to an empty object.
Next, we filter through the array of objects. Filter creates a new array with all elements that pass the test implemented by the provided function.
return array.filter(obj => !uniq[obj.id] && (uniq[obj.id] = true));
Above, we use the short-circuiting functionality of &&. If the left side of the && evaluates to true, then it returns the value on the right of the &&. If the left side is false, it returns what is on the left side of the &&.
For each object(obj) we check uniq for a property named the value of obj.id (In this case, on the first iteration it would check for the property '1'.) We want the opposite of what it returns (either true or false) which is why we use the ! in !uniq[obj.id]. If uniq has the id property already, it returns true which evaluates to false (!) telling the filter function NOT to add that obj. However, if it does not find the obj.id property, it returns false which then evaluates to true (!) and returns everything to the right of the &&, or (uniq[obj.id] = true). This is a truthy value, telling the filter method to add that obj to the returned array, and it also adds the property {1: true} to uniq. This ensures that any other obj instance with that same id will not be added again.
Fast (less runtime) and type-safe answer for lazy Typescript developers:
export const uniqueBy = <T>( uniqueKey: keyof T, objects: T[]): T[] => {
const ids = objects.map(object => object[uniqueKey]);
return objects.filter((object, index) => !ids.includes(object[uniqueKey], index + 1));
}
This way works well for me:
function arrayUnique(arr, uniqueKey) {
const flagList = new Set()
return arr.filter(function(item) {
if (!flagList.has(item[uniqueKey])) {
flagList.add(item[uniqueKey])
return true
}
})
}
const data = [
{
name: 'Kyle',
occupation: 'Fashion Designer'
},
{
name: 'Kyle',
occupation: 'Fashion Designer'
},
{
name: 'Emily',
occupation: 'Web Designer'
},
{
name: 'Melissa',
occupation: 'Fashion Designer'
},
{
name: 'Tom',
occupation: 'Web Developer'
},
{
name: 'Tom',
occupation: 'Web Developer'
}
]
console.table(arrayUnique(data, 'name'))// work well
printout
┌─────────┬───────────┬────────────────────┐
│ (index) │ name │ occupation │
├─────────┼───────────┼────────────────────┤
│ 0 │ 'Kyle' │ 'Fashion Designer' │
│ 1 │ 'Emily' │ 'Web Designer' │
│ 2 │ 'Melissa' │ 'Fashion Designer' │
│ 3 │ 'Tom' │ 'Web Developer' │
└─────────┴───────────┴────────────────────┘
ES5:
function arrayUnique(arr, uniqueKey) {
const flagList = []
return arr.filter(function(item) {
if (flagList.indexOf(item[uniqueKey]) === -1) {
flagList.push(item[uniqueKey])
return true
}
})
}
These two ways are simpler and more understandable.
Here is a solution for ES6 where you only want to keep the last item. This solution is functional and Airbnb style compliant.
const things = {
thing: [
{ place: 'here', name: 'stuff' },
{ place: 'there', name: 'morestuff1' },
{ place: 'there', name: 'morestuff2' },
],
};
const removeDuplicates = (array, key) => {
return array.reduce((arr, item) => {
const removed = arr.filter(i => i[key] !== item[key]);
return [...removed, item];
}, []);
};
console.log(removeDuplicates(things.thing, 'place'));
// > [{ place: 'here', name: 'stuff' }, { place: 'there', name: 'morestuff2' }]
I know there is a ton of answers in this question already, but bear with me...
Some of the objects in your array may have additional properties that you are not interested in, or you simply want to find the unique objects considering only a subset of the properties.
Consider the array below. Say you want to find the unique objects in this array considering only propOne and propTwo, and ignore any other properties that may be there.
The expected result should include only the first and last objects. So here goes the code:
const array = [{
propOne: 'a',
propTwo: 'b',
propThree: 'I have no part in this...'
},
{
propOne: 'a',
propTwo: 'b',
someOtherProperty: 'no one cares about this...'
},
{
propOne: 'x',
propTwo: 'y',
yetAnotherJunk: 'I am valueless really',
noOneHasThis: 'I have something no one has'
}];
const uniques = [...new Set(
array.map(x => JSON.stringify(((o) => ({
propOne: o.propOne,
propTwo: o.propTwo
}))(x))))
].map(JSON.parse);
console.log(uniques);
Another option would be to create a custom indexOf function, which compares the values of your chosen property for each object and wrap this in a reduce function.
var uniq = redundant_array.reduce(function(a,b){
function indexOfProperty (a, b){
for (var i=0;i<a.length;i++){
if(a[i].property == b.property){
return i;
}
}
return -1;
}
if (indexOfProperty(a,b) < 0 ) a.push(b);
return a;
},[]);
Here I found a simple solution for removing duplicates from an array of objects using reduce method. I am filtering elements based on the position key of an object
const med = [
{name: 'name1', position: 'left'},
{name: 'name2', position: 'right'},
{name: 'name3', position: 'left'},
{name: 'name4', position: 'right'},
{name: 'name5', position: 'left'},
{name: 'name6', position: 'left1'}
]
const arr = [];
med.reduce((acc, curr) => {
if(acc.indexOf(curr.position) === -1) {
acc.push(curr.position);
arr.push(curr);
}
return acc;
}, [])
console.log(arr)
If array contains objects, then you can use this to remove duplicate
const persons= [
{ id: 1, name: 'John',phone:'23' },
{ id: 2, name: 'Jane',phone:'23'},
{ id: 1, name: 'Johnny',phone:'56' },
{ id: 4, name: 'Alice',phone:'67' },
];
const unique = [...new Map(persons.map((m) => [m.id, m])).values()];
if remove duplicates on the basis of phone, just replace m.id with m.phone
const unique = [...new Map(persons.map((m) => [m.phone, m])).values()];
I have an object that contains an array of objects.
obj = {};
obj.arr = new Array();
obj.arr.push({place:"here",name:"stuff"});
obj.arr.push({place:"there",name:"morestuff"});
obj.arr.push({place:"there",name:"morestuff"});
I'm wondering what is the best method to remove duplicate objects from an array. So for example, obj.arr would become...
{place:"here",name:"stuff"},
{place:"there",name:"morestuff"}
How about with some es6 magic?
obj.arr = obj.arr.filter((value, index, self) =>
index === self.findIndex((t) => (
t.place === value.place && t.name === value.name
))
)
Reference URL
A more generic solution would be:
const uniqueArray = obj.arr.filter((value, index) => {
const _value = JSON.stringify(value);
return index === obj.arr.findIndex(obj => {
return JSON.stringify(obj) === _value;
});
});
Using the above property strategy instead of JSON.stringify:
const isPropValuesEqual = (subject, target, propNames) =>
propNames.every(propName => subject[propName] === target[propName]);
const getUniqueItemsByProperties = (items, propNames) =>
items.filter((item, index, array) =>
index === array.findIndex(foundItem => isPropValuesEqual(foundItem, item, propNames))
);
You can add a wrapper if you want the propNames property to be either an array or a value:
const getUniqueItemsByProperties = (items, propNames) => {
const propNamesArray = Array.from(propNames);
return items.filter((item, index, array) =>
index === array.findIndex(foundItem => isPropValuesEqual(foundItem, item, propNamesArray))
);
};
allowing both getUniqueItemsByProperties('a') and getUniqueItemsByProperties(['a']);
Stackblitz Example
Explanation
Start by understanding the two methods used:
filter, findIndex
Next take your idea of what makes your two objects equal and keep that in mind.
We can detect something as a duplicate, if it satisfies the criterion that we have just thought of, but it's position is not at the first instance of an object with the criterion.
Therefore we can use the above criterion to determine if something is a duplicate.
One liners with filter ( Preserves order )
Find unique id's in an array.
arr.filter((v,i,a)=>a.findIndex(v2=>(v2.id===v.id))===i)
If the order is not important, map solutions will be faster: Solution with map
Unique by multiple properties ( place and name )
arr.filter((v,i,a)=>a.findIndex(v2=>['place','name'].every(k=>v2[k] ===v[k]))===i)
Unique by all properties (This will be slow for large arrays)
arr.filter((v,i,a)=>a.findIndex(v2=>(JSON.stringify(v2) === JSON.stringify(v)))===i)
Keep the last occurrence by replacing findIndex with findLastIndex.
arr.filter((v,i,a)=>a.findLastIndex(v2=>(v2.place === v.place))===i)
Using ES6+ in a single line you can get a unique list of objects by key:
const key = 'place';
const unique = [...new Map(arr.map(item => [item[key], item])).values()]
It can be put into a function:
function getUniqueListBy(arr, key) {
return [...new Map(arr.map(item => [item[key], item])).values()]
}
Here is a working example:
const arr = [
{place: "here", name: "x", other: "other stuff1" },
{place: "there", name: "x", other: "other stuff2" },
{place: "here", name: "y", other: "other stuff4" },
{place: "here", name: "z", other: "other stuff5" }
]
function getUniqueListBy(arr, key) {
return [...new Map(arr.map(item => [item[key], item])).values()]
}
const arr1 = getUniqueListBy(arr, 'place')
console.log("Unique by place")
console.log(JSON.stringify(arr1))
console.log("\nUnique by name")
const arr2 = getUniqueListBy(arr, 'name')
console.log(JSON.stringify(arr2))
How does it work
First the array is remapped in a way that it can be used as an input for a Map.
arr.map(item => [item[key], item]);
which means each item of the array will be transformed in another array with 2 elements; the selected key as first element and the entire initial item as second element, this is called an entry (ex. array entries, map entries). And here is the official doc with an example showing how to add array entries in Map constructor.
Example when key is place:
[["here", {place: "here", name: "x", other: "other stuff1" }], ...]
Secondly, we pass this modified array to the Map constructor and here is the magic happening. Map will eliminate the duplicate keys values, keeping only last inserted value of the same key.
Note: Map keeps the order of insertion. (check difference between Map and object)
new Map(entry array just mapped above)
Third we use the map values to retrieve the original items, but this time without duplicates.
new Map(mappedArr).values()
And last one is to add those values into a fresh new array so that it can look as the initial structure and return that:
return [...new Map(mappedArr).values()]
Simple and performant solution with a better runtime than the 70+ answers that already exist:
const ids = array.map(o => o.id)
const filtered = array.filter(({id}, index) => !ids.includes(id, index + 1))
Example:
const arr = [{id: 1, name: 'one'}, {id: 2, name: 'two'}, {id: 1, name: 'one'}]
const ids = arr.map(o => o.id)
const filtered = arr.filter(({id}, index) => !ids.includes(id, index + 1))
console.log(filtered)
How it works:
Array.filter() removes all duplicate objects by checking if the previously mapped id-array includes the current id ({id} destructs the object into only its id). To only filter out actual duplicates, it is using Array.includes()'s second parameter fromIndex with index + 1 which will ignore the current object and all previous.
Since every iteration of the filter callback method will only search the array beginning at the current index + 1, this also dramatically reduces the runtime because only objects not previously filtered get checked.
This obviously also works for any other key that is not called id, multiple or even all keys.
A primitive method would be:
const obj = {};
for (let i = 0, len = things.thing.length; i < len; i++) {
obj[things.thing[i]['place']] = things.thing[i];
}
things.thing = new Array();
for (const key in obj) {
things.thing.push(obj[key]);
}
If you can use Javascript libraries such as underscore or lodash, I recommend having a look at _.uniq function in their libraries. From lodash:
_.uniq(array, [isSorted=false], [callback=_.identity], [thisArg])
Basically, you pass in the array that in here is an object literal and you pass in the attribute that you want to remove duplicates with in the original data array, like this:
var data = [{'name': 'Amir', 'surname': 'Rahnama'}, {'name': 'Amir', 'surname': 'Stevens'}];
var non_duplidated_data = _.uniq(data, 'name');
UPDATE: Lodash now has introduced a .uniqBy as well.
I had this exact same requirement, to remove duplicate objects in a array, based on duplicates on a single field. I found the code here: Javascript: Remove Duplicates from Array of Objects
So in my example, I'm removing any object from the array that has a duplicate licenseNum string value.
var arrayWithDuplicates = [
{"type":"LICENSE", "licenseNum": "12345", state:"NV"},
{"type":"LICENSE", "licenseNum": "A7846", state:"CA"},
{"type":"LICENSE", "licenseNum": "12345", state:"OR"},
{"type":"LICENSE", "licenseNum": "10849", state:"CA"},
{"type":"LICENSE", "licenseNum": "B7037", state:"WA"},
{"type":"LICENSE", "licenseNum": "12345", state:"NM"}
];
function removeDuplicates(originalArray, prop) {
var newArray = [];
var lookupObject = {};
for(var i in originalArray) {
lookupObject[originalArray[i][prop]] = originalArray[i];
}
for(i in lookupObject) {
newArray.push(lookupObject[i]);
}
return newArray;
}
var uniqueArray = removeDuplicates(arrayWithDuplicates, "licenseNum");
console.log("uniqueArray is: " + JSON.stringify(uniqueArray));
The results:
uniqueArray is:
[{"type":"LICENSE","licenseNum":"10849","state":"CA"},
{"type":"LICENSE","licenseNum":"12345","state":"NM"},
{"type":"LICENSE","licenseNum":"A7846","state":"CA"},
{"type":"LICENSE","licenseNum":"B7037","state":"WA"}]
One liner using Set
var things = new Object();
things.thing = new Array();
things.thing.push({place:"here",name:"stuff"});
things.thing.push({place:"there",name:"morestuff"});
things.thing.push({place:"there",name:"morestuff"});
// assign things.thing to myData for brevity
var myData = things.thing;
things.thing = Array.from(new Set(myData.map(JSON.stringify))).map(JSON.parse);
console.log(things.thing)
Explanation:
new Set(myData.map(JSON.stringify)) creates a Set object using the stringified myData elements.
Set object will ensure that every element is unique.
Then I create an array based on the elements of the created set using Array.from.
Finally, I use JSON.parse to convert stringified element back to an object.
ES6 one liner is here
let arr = [
{id:1,name:"sravan ganji"},
{id:2,name:"pinky"},
{id:4,name:"mammu"},
{id:3,name:"avy"},
{id:3,name:"rashni"},
];
console.log(Object.values(arr.reduce((acc,cur)=>Object.assign(acc,{[cur.id]:cur}),{})))
To remove all duplicates from an array of objects, the simplest way is use filter:
var uniq = {};
var arr = [{"id":"1"},{"id":"1"},{"id":"2"}];
var arrFiltered = arr.filter(obj => !uniq[obj.id] && (uniq[obj.id] = true));
console.log('arrFiltered', arrFiltered);
One liners with Map ( High performance, Does not preserve order )
Find unique id's in array arr.
const arrUniq = [...new Map(arr.map(v => [v.id, v])).values()]
If the order is important check out the solution with filter: Solution with filter
Unique by multiple properties ( place and name ) in array arr
const arrUniq = [...new Map(arr.map(v => [JSON.stringify([v.place,v.name]), v])).values()]
Unique by all properties in array arr
const arrUniq = [...new Map(arr.map(v => [JSON.stringify(v), v])).values()]
Keep the first occurrence in array arr
const arrUniq = [...new Map(arr.slice().reverse().map(v => [v.id, v])).values()].reverse()
Here's another option to do it using Array iterating methods if you need comparison only by one field of an object:
function uniq(a, param){
return a.filter(function(item, pos, array){
return array.map(function(mapItem){ return mapItem[param]; }).indexOf(item[param]) === pos;
})
}
uniq(things.thing, 'place');
This is a generic way of doing this: you pass in a function that tests whether two elements of an array are considered equal. In this case, it compares the values of the name and place properties of the two objects being compared.
ES5 answer
function removeDuplicates(arr, equals) {
var originalArr = arr.slice(0);
var i, len, val;
arr.length = 0;
for (i = 0, len = originalArr.length; i < len; ++i) {
val = originalArr[i];
if (!arr.some(function(item) { return equals(item, val); })) {
arr.push(val);
}
}
}
function thingsEqual(thing1, thing2) {
return thing1.place === thing2.place
&& thing1.name === thing2.name;
}
var things = [
{place:"here",name:"stuff"},
{place:"there",name:"morestuff"},
{place:"there",name:"morestuff"}
];
removeDuplicates(things, thingsEqual);
console.log(things);
Original ES3 answer
function arrayContains(arr, val, equals) {
var i = arr.length;
while (i--) {
if ( equals(arr[i], val) ) {
return true;
}
}
return false;
}
function removeDuplicates(arr, equals) {
var originalArr = arr.slice(0);
var i, len, j, val;
arr.length = 0;
for (i = 0, len = originalArr.length; i < len; ++i) {
val = originalArr[i];
if (!arrayContains(arr, val, equals)) {
arr.push(val);
}
}
}
function thingsEqual(thing1, thing2) {
return thing1.place === thing2.place
&& thing1.name === thing2.name;
}
removeDuplicates(things.thing, thingsEqual);
If you can wait to eliminate the duplicates until after all the additions, the typical approach is to first sort the array and then eliminate duplicates. The sorting avoids the N * N approach of scanning the array for each element as you walk through them.
The "eliminate duplicates" function is usually called unique or uniq. Some existing implementations may combine the two steps, e.g., prototype's uniq
This post has few ideas to try (and some to avoid :-) ) if your library doesn't already have one! Personally I find this one the most straight forward:
function unique(a){
a.sort();
for(var i = 1; i < a.length; ){
if(a[i-1] == a[i]){
a.splice(i, 1);
} else {
i++;
}
}
return a;
}
// Provide your own comparison
function unique(a, compareFunc){
a.sort( compareFunc );
for(var i = 1; i < a.length; ){
if( compareFunc(a[i-1], a[i]) === 0){
a.splice(i, 1);
} else {
i++;
}
}
return a;
}
I think the best approach is using reduce and Map object. This is a single line solution.
const data = [
{id: 1, name: 'David'},
{id: 2, name: 'Mark'},
{id: 2, name: 'Lora'},
{id: 4, name: 'Tyler'},
{id: 4, name: 'Donald'},
{id: 5, name: 'Adrian'},
{id: 6, name: 'Michael'}
]
const uniqueData = [...data.reduce((map, obj) => map.set(obj.id, obj), new Map()).values()];
console.log(uniqueData)
/*
in `map.set(obj.id, obj)`
'obj.id' is key. (don't worry. we'll get only values using the .values() method)
'obj' is whole object.
*/
To add one more to the list. Using ES6 and Array.reduce with Array.find.
In this example filtering objects based on a guid property.
let filtered = array.reduce((accumulator, current) => {
if (! accumulator.find(({guid}) => guid === current.guid)) {
accumulator.push(current);
}
return accumulator;
}, []);
Extending this one to allow selection of a property and compress it into a one liner:
const uniqify = (array, key) => array.reduce((prev, curr) => prev.find(a => a[key] === curr[key]) ? prev : prev.push(curr) && prev, []);
To use it pass an array of objects and the name of the key you wish to de-dupe on as a string value:
const result = uniqify(myArrayOfObjects, 'guid')
Considering lodash.uniqWith
const objects = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }, { 'x': 1, 'y': 2 }];
_.uniqWith(objects, _.isEqual);
// => [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }]
You could also use a Map:
const dedupThings = Array.from(things.thing.reduce((m, t) => m.set(t.place, t), new Map()).values());
Full sample:
const things = new Object();
things.thing = new Array();
things.thing.push({place:"here",name:"stuff"});
things.thing.push({place:"there",name:"morestuff"});
things.thing.push({place:"there",name:"morestuff"});
const dedupThings = Array.from(things.thing.reduce((m, t) => m.set(t.place, t), new Map()).values());
console.log(JSON.stringify(dedupThings, null, 4));
Result:
[
{
"place": "here",
"name": "stuff"
},
{
"place": "there",
"name": "morestuff"
}
]
Dang, kids, let's crush this thing down, why don't we?
let uniqIds = {}, source = [{id:'a'},{id:'b'},{id:'c'},{id:'b'},{id:'a'},{id:'d'}];
let filtered = source.filter(obj => !uniqIds[obj.id] && (uniqIds[obj.id] = true));
console.log(filtered);
// EXPECTED: [{id:'a'},{id:'b'},{id:'c'},{id:'d'}];
let myData = [{place:"here",name:"stuff"},
{place:"there",name:"morestuff"},
{place:"there",name:"morestuff"}];
let q = [...new Map(myData.map(obj => [JSON.stringify(obj), obj])).values()];
console.log(q)
One-liner using ES6 and new Map().
// assign things.thing to myData
let myData = things.thing;
[...new Map(myData.map(obj => [JSON.stringify(obj), obj])).values()];
Details:-
Doing .map() on the data list and converting each individual object into a [key, value] pair array(length =2), the first element(key) would be the stringified version of the object and second(value) would be an object itself.
Adding above created array list to new Map() would have the key as stringified object and any same key addition would result in overriding the already existing key.
Using .values() would give MapIterator with all values in a Map (obj in our case)
Finally, spread ... operator to give new Array with values from the above step.
A TypeScript solution
This will remove duplicate objects and also preserve the types of the objects.
function removeDuplicateObjects(array: any[]) {
return [...new Set(array.map(s => JSON.stringify(s)))]
.map(s => JSON.parse(s));
}
const things = [
{place:"here",name:"stuff"},
{place:"there",name:"morestuff"},
{place:"there",name:"morestuff"}
];
const filteredArr = things.reduce((thing, current) => {
const x = thing.find(item => item.place === current.place);
if (!x) {
return thing.concat([current]);
} else {
return thing;
}
}, []);
console.log(filteredArr)
Solution Via Set Object | According to the data type
const seen = new Set();
const things = [
{place:"here",name:"stuff"},
{place:"there",name:"morestuff"},
{place:"there",name:"morestuff"}
];
const filteredArr = things.filter(el => {
const duplicate = seen.has(el.place);
seen.add(el.place);
return !duplicate;
});
console.log(filteredArr)
Set Object Feature
Each value in the Set Object has to be unique, the value equality will be checked
The Purpose of Set object storing unique values according to the Data type , whether primitive values or object references.it has very useful four Instance methods add, clear , has & delete.
Unique & data Type feature:..
addmethod
it's push unique data into collection by default also preserve data type .. that means it prevent to push duplicate item into collection also it will check data type by default...
has method
sometime needs to check data item exist into the collection and . it's handy method for the collection to cheek unique id or item and data type..
delete method
it will remove specific item from the collection by identifying data type..
clear method
it will remove all collection items from one specific variable and set as empty object
Set object has also Iteration methods & more feature..
Better Read from Here : Set - JavaScript | MDN
removeDuplicates() takes in an array of objects and returns a new array without any duplicate objects (based on the id property).
const allTests = [
{name: 'Test1', id: '1'},
{name: 'Test3', id: '3'},
{name: 'Test2', id: '2'},
{name: 'Test2', id: '2'},
{name: 'Test3', id: '3'}
];
function removeDuplicates(array) {
let uniq = {};
return array.filter(obj => !uniq[obj.id] && (uniq[obj.id] = true))
}
removeDuplicates(allTests);
Expected outcome:
[
{name: 'Test1', id: '1'},
{name: 'Test3', id: '3'},
{name: 'Test2', id: '2'}
];
First, we set the value of variable uniq to an empty object.
Next, we filter through the array of objects. Filter creates a new array with all elements that pass the test implemented by the provided function.
return array.filter(obj => !uniq[obj.id] && (uniq[obj.id] = true));
Above, we use the short-circuiting functionality of &&. If the left side of the && evaluates to true, then it returns the value on the right of the &&. If the left side is false, it returns what is on the left side of the &&.
For each object(obj) we check uniq for a property named the value of obj.id (In this case, on the first iteration it would check for the property '1'.) We want the opposite of what it returns (either true or false) which is why we use the ! in !uniq[obj.id]. If uniq has the id property already, it returns true which evaluates to false (!) telling the filter function NOT to add that obj. However, if it does not find the obj.id property, it returns false which then evaluates to true (!) and returns everything to the right of the &&, or (uniq[obj.id] = true). This is a truthy value, telling the filter method to add that obj to the returned array, and it also adds the property {1: true} to uniq. This ensures that any other obj instance with that same id will not be added again.
Fast (less runtime) and type-safe answer for lazy Typescript developers:
export const uniqueBy = <T>( uniqueKey: keyof T, objects: T[]): T[] => {
const ids = objects.map(object => object[uniqueKey]);
return objects.filter((object, index) => !ids.includes(object[uniqueKey], index + 1));
}
This way works well for me:
function arrayUnique(arr, uniqueKey) {
const flagList = new Set()
return arr.filter(function(item) {
if (!flagList.has(item[uniqueKey])) {
flagList.add(item[uniqueKey])
return true
}
})
}
const data = [
{
name: 'Kyle',
occupation: 'Fashion Designer'
},
{
name: 'Kyle',
occupation: 'Fashion Designer'
},
{
name: 'Emily',
occupation: 'Web Designer'
},
{
name: 'Melissa',
occupation: 'Fashion Designer'
},
{
name: 'Tom',
occupation: 'Web Developer'
},
{
name: 'Tom',
occupation: 'Web Developer'
}
]
console.table(arrayUnique(data, 'name'))// work well
printout
┌─────────┬───────────┬────────────────────┐
│ (index) │ name │ occupation │
├─────────┼───────────┼────────────────────┤
│ 0 │ 'Kyle' │ 'Fashion Designer' │
│ 1 │ 'Emily' │ 'Web Designer' │
│ 2 │ 'Melissa' │ 'Fashion Designer' │
│ 3 │ 'Tom' │ 'Web Developer' │
└─────────┴───────────┴────────────────────┘
ES5:
function arrayUnique(arr, uniqueKey) {
const flagList = []
return arr.filter(function(item) {
if (flagList.indexOf(item[uniqueKey]) === -1) {
flagList.push(item[uniqueKey])
return true
}
})
}
These two ways are simpler and more understandable.
Here is a solution for ES6 where you only want to keep the last item. This solution is functional and Airbnb style compliant.
const things = {
thing: [
{ place: 'here', name: 'stuff' },
{ place: 'there', name: 'morestuff1' },
{ place: 'there', name: 'morestuff2' },
],
};
const removeDuplicates = (array, key) => {
return array.reduce((arr, item) => {
const removed = arr.filter(i => i[key] !== item[key]);
return [...removed, item];
}, []);
};
console.log(removeDuplicates(things.thing, 'place'));
// > [{ place: 'here', name: 'stuff' }, { place: 'there', name: 'morestuff2' }]
I know there is a ton of answers in this question already, but bear with me...
Some of the objects in your array may have additional properties that you are not interested in, or you simply want to find the unique objects considering only a subset of the properties.
Consider the array below. Say you want to find the unique objects in this array considering only propOne and propTwo, and ignore any other properties that may be there.
The expected result should include only the first and last objects. So here goes the code:
const array = [{
propOne: 'a',
propTwo: 'b',
propThree: 'I have no part in this...'
},
{
propOne: 'a',
propTwo: 'b',
someOtherProperty: 'no one cares about this...'
},
{
propOne: 'x',
propTwo: 'y',
yetAnotherJunk: 'I am valueless really',
noOneHasThis: 'I have something no one has'
}];
const uniques = [...new Set(
array.map(x => JSON.stringify(((o) => ({
propOne: o.propOne,
propTwo: o.propTwo
}))(x))))
].map(JSON.parse);
console.log(uniques);
Another option would be to create a custom indexOf function, which compares the values of your chosen property for each object and wrap this in a reduce function.
var uniq = redundant_array.reduce(function(a,b){
function indexOfProperty (a, b){
for (var i=0;i<a.length;i++){
if(a[i].property == b.property){
return i;
}
}
return -1;
}
if (indexOfProperty(a,b) < 0 ) a.push(b);
return a;
},[]);
Here I found a simple solution for removing duplicates from an array of objects using reduce method. I am filtering elements based on the position key of an object
const med = [
{name: 'name1', position: 'left'},
{name: 'name2', position: 'right'},
{name: 'name3', position: 'left'},
{name: 'name4', position: 'right'},
{name: 'name5', position: 'left'},
{name: 'name6', position: 'left1'}
]
const arr = [];
med.reduce((acc, curr) => {
if(acc.indexOf(curr.position) === -1) {
acc.push(curr.position);
arr.push(curr);
}
return acc;
}, [])
console.log(arr)
If array contains objects, then you can use this to remove duplicate
const persons= [
{ id: 1, name: 'John',phone:'23' },
{ id: 2, name: 'Jane',phone:'23'},
{ id: 1, name: 'Johnny',phone:'56' },
{ id: 4, name: 'Alice',phone:'67' },
];
const unique = [...new Map(persons.map((m) => [m.id, m])).values()];
if remove duplicates on the basis of phone, just replace m.id with m.phone
const unique = [...new Map(persons.map((m) => [m.phone, m])).values()];
How one can write a function, which takes only few attributes in most-compact way in ES6?
I've came up with solution using destructuring + simplified object literal, but I don't like that list of fields is repeated in the code.
Is there an even slimmer solution?
(v) => {
let { id, title } = v;
return { id, title };
}
Here's something slimmer, although it doesn't avoid repeating the list of fields. It uses "parameter destructuring" to avoid the need for the v parameter.
({id, title}) => ({id, title})
(See a runnable example in this other answer).
#EthanBrown's solution is more general. Here is a more idiomatic version of it which uses Object.assign, and computed properties (the [p] part):
function pick(o, ...props) {
return Object.assign({}, ...props.map(prop => ({[prop]: o[prop]})));
}
If we want to preserve the properties' attributes, such as configurable and getters and setters, while also omitting non-enumerable properties, then:
function pick(o, ...props) {
var has = p => o.propertyIsEnumerable(p),
get = p => Object.getOwnPropertyDescriptor(o, p);
return Object.defineProperties({},
Object.assign({}, ...props
.filter(prop => has(prop))
.map(prop => ({prop: get(props)})))
);
}
I don't think there's any way to make it much more compact than your answer (or torazburo's), but essentially what you're trying to do is emulate Underscore's pick operation. It would be easy enough to re-implement that in ES6:
function pick(o, ...fields) {
return fields.reduce((a, x) => {
if(o.hasOwnProperty(x)) a[x] = o[x];
return a;
}, {});
}
Then you have a handy re-usable function:
var stuff = { name: 'Thing', color: 'blue', age: 17 };
var picked = pick(stuff, 'name', 'age');
The trick to solving this as a one-liner is to flip the approach taken: Instead of starting from original object orig, one can start from the keys they want to extract.
Using Array#reduce one can then store each needed key on the empty object which is passed in as the initialValue for said function.
Like so:
const orig = {
id: 123456789,
name: 'test',
description: '…',
url: 'https://…',
};
const filtered = ['id', 'name'].reduce((result, key) => { result[key] = orig[key]; return result; }, {});
console.log(filtered); // Object {id: 123456789, name: "test"}
alternatively...
const filtered = ['id', 'name'].reduce((result, key) => ({
...result,
[key]: orig[key]
}), {});
console.log(filtered); // Object {id: 123456789, name: "test"}
A tiny bit shorter solution using the comma operator:
const pick = (O, ...K) => K.reduce((o, k) => (o[k]=O[k], o), {})
console.log(
pick({ name: 'John', age: 29, height: 198 }, 'name', 'age')
)
ES6 was the latest spec at the time when the question was written. As explained in this answer, key picking is significantly shorter in ES2019 than in ES6:
Object.fromEntries(
Object.entries(obj)
.filter(([key]) => ['foo', 'bar'].includes(key))
)
TC39's object rest/spread properties proposal will make this pretty slick:
let { x, y, ...z } = { x: 1, y: 2, a: 3, b: 4 };
z; // { a: 3, b: 4 }
(It does have the downside of creating the x and y variables which you may not need.)
You can use object destructuring to unpack properties from the existing object and assign them to variables with different names - fields of a new, initially empty object.
const person = {
fname: 'tom',
lname: 'jerry',
aage: 100,
}
let newPerson = {};
({fname: newPerson.fname, lname: newPerson.lname} = person);
console.log(newPerson);
There's currently a strawman proposal for improving JavaScript's object shorthand syntax, which would enable "picking" of named properties without repetition:
const source = {id: "68646", genre: "crime", title: "Scarface"};
const target = {};
Object.assign(target, {source.title, source.id});
console.log(picked);
// {id: "68646", title: "Scarface"}
Unfortunately, the proposal doesn't seem to be going anywhere any time soon. Last edited in July 2017 and still a draft at Stage 0, suggesting the author may have ditched or forgotten about it.
ES5 and earlier (non-strict mode)
The concisest possible shorthand I can think of involves an ancient language feature nobody uses anymore:
Object.assign(target, {...(o => {
with(o) return { id, title };
})(source)});
with statements are forbidden in strict mode, making this approach useless for 99.999% of modern JavaScript. Bit of a shame, because this is the only halfway-decent use I've found for the with feature. 😀
I have similar to Ethan Brown's solution, but even shorter - pick function. Another function pick2 is a bit longer (and slower), but allows to rename properties in the similar to ES6 manner.
const pick = (o, ...props) => props.reduce((r, p) => p in o ? {...r, [p]: o[p]} : r, {})
const pick2 = (o, ...props) => props.reduce((r, expr) => {
const [p, np] = expr.split(":").map( e => e.trim() )
return p in o ? {...r, [np || p]: o[p]} : r
}, {})
Here is the usage example:
const d = { a: "1", c: "2" }
console.log(pick(d, "a", "b", "c")) // -> { a: "1", c: "2" }
console.log(pick2(d, "a: x", "b: y", "c")) // -> { x: "1", c: "2" }
I required this sollution but I didn't knew if the proposed keys were available. So, I took #torazaburo answer and improved for my use case:
function pick(o, ...props) {
return Object.assign({}, ...props.map(prop => {
if (o[prop]) return {[prop]: o[prop]};
}));
}
// Example:
var person = { name: 'John', age: 29 };
var myObj = pick(person, 'name', 'sex'); // { name: 'John' }
Some great solutions above, didn't see one for Typescript fleshed out, so here it goes. Based on #Ethan Browns solution above
const pick = < T extends object, K extends keyof T >(
obj: T,
...keys: K[]
): Pick< T, K > =>
keys.reduce< any >( ( r, key ) => {
r[ key ] = obj[ key ];
return r;
}, {} );
And for bonus, here is TS friendly es6 omit, and one that is much more performant below, but less es6.
const omit = < T extends object, K extends keyof T >(
obj: T,
...keys: K[]
): Omit< T, K > =>
keys.reduce( ( r, key ) => ( delete r[ key ], r ), {
...obj,
} );
Way more performant omit: http://jsben.ch/g6QCK
const omit = < T extends object, K extends keyof T >(
obj: T,
...keys: K[]
): Omit< T, K > => {
let r: any = {};
let length = keys.length;
while ( length-- ) {
const key = keys[ length ];
r[ key ] = obj[ key ];
}
return r;
};
Here's my solution:
originalObject = {a:1, b:2, c:3, d:4, e:5}
picks = ["b", "d"]
Object.fromEntries(Object.entries(originalObject).filter(entry => picks.includes(entry[0])))
which results in
{b: 2, d: 4}
inspired by the reduce approach of https://stackoverflow.com/users/865693/shesek:
const pick = (orig, keys) => keys.reduce((acc, key) => ({...acc, [key]: orig[key]}), {})
or even slightly shorter using the comma operator (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comma_Operator)
const pick = (obj, keys) => keys.reduce((acc, key) => ((acc[key] = obj[key]), acc), {});
usage:
pick({ model : 'F40', manufacturer: 'Ferrari', productionYear: 1987 }, 'model', 'productionYear')
results in:
{model: "F40", productionYear: 1987}