JavaScript merging objects by id [duplicate] - javascript

This question already has answers here:
Most efficient method to groupby on an array of objects
(58 answers)
How to merge two arrays in JavaScript and de-duplicate items
(89 answers)
Closed 7 months ago.
What's the correct way to merge two arrays in Javascript?
I've got two arrays (for example):
var a1 = [{ id : 1, name : "test"}, { id : 2, name : "test2"}]
var a2 = [{ id : 1, count : "1"}, {id : 2, count : "2"}]
I want to be able to end up with something like:
var a3 = [{ id : 1, name : "test", count : "1"},
{ id : 2, name : "test2", count : "2"}]
Where the two arrays are being joined based on the 'id' field and extra data is simply being added.
I tried to use _.union to do this, but it simply overwrites the values from the second array into the first one

Short ES6 solution
const a3 = a1.map(t1 => ({...t1, ...a2.find(t2 => t2.id === t1.id)}))

This should do the trick:
var mergedList = _.map(a1, function(item){
return _.extend(item, _.findWhere(a2, { id: item.id }));
});
This assumes that the id of the second object in a1 should be 2 rather than "2"

Assuming IDs are strings and the order does not matter, you can
Create a hash table.
Iterate both arrays and store the data in the hash table, indexed by the ID. If there already is some data with that ID, update it with Object.assign (ES6, can be polyfilled).
Get an array with the values of the hash map.
var hash = Object.create(null);
a1.concat(a2).forEach(function(obj) {
hash[obj.id] = Object.assign(hash[obj.id] || {}, obj);
});
var a3 = Object.keys(hash).map(function(key) {
return hash[key];
});
In ECMAScript6, if the IDs are not necessarily strings, you can use Map:
var hash = new Map();
a1.concat(a2).forEach(function(obj) {
hash.set(obj.id, Object.assign(hash.get(obj.id) || {}, obj))
});
var a3 = Array.from(hash.values());

ES6 simplifies this:
let merge = (obj1, obj2) => ({...obj1, ...obj2});
Note that repeated keys will be merged, and the value of the second object will prevail and the repeated value of the first object will be ignored.
Example:
let obj1 = {id: 1, uniqueObj1Key: "uniqueKeyValueObj1", repeatedKey: "obj1Val"};
let obj2 = {id: 1, uniqueObj2Key: "uniqueKeyValueObj2", repeatedKey: "obj2Val"};
merge(obj1, obj2)
// {id: 1, uniqueObj1Key: "uniqueKeyValueObj1", repeatedKey: "obj2Val", uniqueObj2Key: "uniqueKeyValueObj2"}
merge(obj2, obj1)
// {id: 1, uniqueObj2Key: "uniqueKeyValueObj2", repeatedKey: "obj1Val", uniqueObj1Key: "uniqueKeyValueObj1"}
Complete solution (with Lodash, not Underscore)
var a1 = [{ id : 1, name : "test"}, { id : 2, name : "test2"}]
var a2 = [{ id : 1, count : "1"}, {id : 2, count : "2"}]
var merge = (obj1, obj2) => ({...obj1, ...obj2});
_.zipWith(a1, a2, merge)
(2) [{…}, {…}]
0: {id: 1, name: "test", count: "1"}
1: {id: 2, name: "test2", count: "2"}
If you have an array of arrays to merge you can do it like this:
var arrayOfArraysToMerge = [a1, a2, a3, a4]; //a3 and a4 are arrays like a1 and a2 but with different properties and same IDs.
_.zipWith(...arrayOfArraysToMerge, merge)
(2) [{…}, {…}]
0: {id: 1, name: "test", count: "1", extra1: "val1", extra2: 1}
1: {id: 2, name: "test2", count: "2", extra1: "val2", extra2: 2}

reduce version.
var a3 = a1.concat(a2).reduce((acc, x) => {
acc[x.id] = Object.assign(acc[x.id] || {}, x);
return acc;
}, {});
_.values(a3);
I think it's common practice in functional language.

Already there are many great answers, I'll just add another one which is from a real problem I needed to solve yesterday.
I had an array of messages with user ids, and one array of users containing users' names and other details. This is how I managed to add user details to the messages.
var messages = [{userId: 2, content: "Salam"}, {userId: 5, content: "Hello"},{userId: 4, content: "Moi"}];
var users = [{id: 2, name: "Grace"}, {id: 4, name: "Janetta"},{id: 5, name: "Sara"}];
var messagesWithUserNames = messages.map((msg)=> {
var haveEqualId = (user) => user.id === msg.userId
var userWithEqualId= users.find(haveEqualId)
return Object.assign({}, msg, userWithEqualId)
})
console.log(messagesWithUserNames)

Vanilla JS solution
const a1 = [{ id : 1, name : "test"}, { id : 2, name : "test2"}]
const a2 = [{ id : 1, count : "1"}, {id : 2, count : "2"}]
const merge = (arr1, arr2) => {
const temp = []
arr1.forEach(x => {
arr2.forEach(y => {
if (x.id === y.id) {
temp.push({ ...x, ...y })
}
})
})
return temp
}
console.log(merge(a1, a2))

The lodash implementaiton:
var merged = _.map(a1, function(item) {
return _.assign(item, _.find(a2, ['id', item.id]));
});
The result:
[
{
"id":1,
"name":"test",
"count":"1"
},
{
"id":2,
"name":"test2",
"count":"2"
}
]

Wanted to add this answer which is derived from #daisihi answer above. Main difference is that this uses the spread operator.
Also, at the end I remove the id because it was not desirable in the first place.
const a3 = [...a1, ...a2].reduce((acc, x) => {
acc[x.id] = {...acc[x.id] || {}, ...x};
return acc;
}, {});
This part was taken from another post. removing a property from a list of objects in an array
const newArray = Object.values(a3).map(({id, ...keepAttrs}) => keepAttrs);

Found other solutions failing for some cases, so writing a better one here
const a1 = [{ id : 1, name : "test"}, { id : 2, name : "test2"}]
const a2 = [{ id : 3, count : "3"}, { id : 1, count : "1"}, {id : 2, count : "2"}]
const mergeHelper = new Map(a1.map(x => [x.id, x]));
for (const x of a2) {
if (mergeHelper.has(x.id)) {
const item = mergeHelper.get(x.id);
mergeHelper.set(x.id, {...item, ...x});
} else {
mergeHelper.set(x.id, x);
}
}
const mergedList = [...mergeHelper.values()];
// For sorted array
// const mergedSortedList = [...mergeHelper.values()].sort((a, b) => a.id - b.id);
console.log(mergedList)
Using js Map is way faster than other approaches, helps when array length is huge.

A working TypeScript version:
export default class Merge {
static byKey(a1: any[], a2: any[], key: string) {
const res = a1.concat(a2).reduce((acc, x) => {
acc[x[key]] = Object.assign(acc[x[key]] || {}, x);
return acc;
}, {});
return Object.entries(res).map(pair => {
const [, value] = pair;
return value;
});
}
}
test("Merge", async () => {
const a1 = [{ id: "1", value: "1" }, { id: "2", value: "2" }];
const a2 = [{ id: "2", value: "3" }];
expect(Merge.byKey(a1, a2, "id")).toStrictEqual([
{
id: "1",
value: "1"
},
{ id: "2", value: "3" }
]);
});

try this
var a1 = [{ id : 1, name : "test"}, { id : 2, name : "test2"}]
var a2 = [{ id : 1, count : "1"}, {id : 2, count : "2"}]
let arr3 = a1.map((item, i) => Object.assign({}, item, a2[i]));
console.log(arr3);

How about this?
const mergeArrayObjects = (arr1: any[], arr2: any[], mergeByKey: string): any[] => {
const updatedArr = [];
for (const obj of arr1) {
const arr1ValueInArr2 = arr2.find(
a => a[mergeByKey] === obj[mergeByKey],
);
if (arr1ValueInArr2) {
updatedArr.push(Object.assign(obj, arr1ValueInArr2));
} else {
updatedArr.push(obj);
}
}
const mergeByKeyValuesInArr1 = arr1.map(a => a[mergeByKey]);
const remainingObjInArr2 = arr2.filter(a => !mergeByKeyValuesInArr1.includes(a[mergeByKey]) )
return updatedArr.concat(remainingObjInArr2)
}

You can write a simple object merging function like this
function mergeObject(cake, icing) {
var icedCake = {}, ingredient;
for (ingredient in cake)
icedCake[ingredient] = cake[ingredient];
for (ingredient in icing)
icedCake[ingredient] = icing[ingredient];
return icedCake;
}
Next, you need to do use a double-loop to apply it to your data structre
var i, j, a3 = a1.slice();
for (i = 0; i < a2.length; ++i) // for each item in a2
for (j = 0; i < a3.length; ++i) // look at items in other array
if (a2[i]['id'] === a3[j]['id']) // if matching id
a3[j] = mergeObject(a3[j], a2[i]); // merge
You can also use mergeObject as a simple clone, too, by passing one parameter as an empty object.

const a3 = a1.map(it1 => {
it1.test = a2.find(it2 => it2.id === it1.id).test
return it1
})

If you have exactly the same number of items in both array with same ids you could do something like this.
const mergedArr = arr1.map((item, i) => {
if (item.ID === arr2[i].ID) {
return Object.assign({}, item, arr2[i]);
}
});

function mergeDiffs(Schedulearray1, Schedulearray2) {
var secondArrayIDs = Schedulearray2.map(x=> x.scheduleid);
return Schedulearray1.filter(x=> !secondArrayIDs.includes(x.scheduleid)).concat(Schedulearray2);
}

None of them worked for me. I wrote own:
const formatteddata=data.reduce((a1,a2)=>{
for (let t=0; t<a1.length; t++)
{var id1=a1[t].id
for (let tt=0; tt<a2.length; tt++)
{var id2=a2[tt].id
if(id1==date2)
{a1[t]={...a1[t],...a2[tt]}}
}
}
return a1
})
works with any amount of arrays of objects in arrays, with varying length and not always coinsciding dates

Related

Diffing 2 arrays, i.e which items to add and remove

Given:
array of "current" objects [{id: '1'},{id: '2'},{id: '3'},{id: '4'},{id: '5'}]
array of "new" objects [{id: '1'},{id: '5'},{id: '6'},{id: '7'},{id: '8'}]
How to determine which objects
to add (not in "current") and
to remove (not in "new")?
In this case:
{id: '2'},{id: '3'},{id: '4'} should be removed
{id: '6'},{id: '7'},{id: '8'} should be added
Performance is not extremely important, my dataset is usually around 200.
Edit: I need to know about elements to be added/removed because "current" array correlates to DOM nodes and I don't just want to delete them all and add from scratch - already tried that and performance is far from perfect.
In both cases, this is a classic use case of the set operation "difference". All you need to do is define a difference function, and then apply it with current.difference(new) and new.difference(current).
function difference(a, b, compare) {
let diff = [];
for (let ai = 0; ai < a.length; ai++) {
let exists = false;
for (let bi = 0; bi < b.length; bi++) {
if (compare(a[ai], b[bi])) {
exists = true;
break;
}
}
if (!exists) diff.push(a[ai]);
}
return diff;
}
function getRemoved(oldA, newA) {
return difference(oldA, newA, (a, b) => a.id == b.id);
}
function getAdded(oldA, newA) {
return difference(newA, oldA, (a, b) => a.id == b.id);
}
let current = [{id: '1'}, {id: '2'}, {id: '3'}, {id: '4'}, {id: '5'}];
let newArr = [{id: '1'}, {id: '5'}, {id: '6'}, {id: '7'}, {id: '8'}];
console.log(getRemoved(current, newArr));
console.log(getAdded(current, newArr));
For each item in current that is not found in new. That item is deleted.
For each item in new that is not in current. That item is added.
You run the run these checks in two different loops one after the other.
You could go with something like this :
const currentElements = [{id: '1'},{id: '2'},{id: '3'},{id: '4'},{id: '5'}]
const newElements = [{id: '1'},{id: '5'},{id: '6'},{id: '7'},{id: '8'}]
const elementsToAdd = newElements.filter(e1 => !currentElements.find(e2 => e2.id === e1.id))
const elementsToRemove = currentElements.filter(e1 => !newElements.find(e2 => e2.id === e1.id))
console.log({elementsToAdd, elementsToRemove})
Basically, I take an array, and find the elements not contained in the other array.
For the elements to add, check the elements in newElements that are not in currentElements, and vice versa.
You could use Set data structure to calculate the different and filter the expeced values (using its has method). Time complexity for this approach will be linear (O(n))
Set use hash table so complexity for retrival/lookup is O(1) (doc, Theory > Lookup Speed)
const prev = [{ id: "1" }, { id: "2" }, { id: "3" }, { id: "4" }, { id: "5" }] // length n
const current = [
{ id: "1" },
{ id: "5" },
{ id: "6" },
{ id: "7" },
{ id: "8" },
] // length m
const prevIdSet = new Set(prev.map((o) => o.id)) // O(n)
const currentIdSet = new Set(current.map((o) => o.id)) // O(m)
function difference(setA, setB) {
let _difference = new Set(setA)
for (let elem of setB) {
_difference.delete(elem)
}
return _difference
}
const removedIdSet = difference(prevIdSet, currentIdSet) // O(m)
const addedIdSet = difference(currentIdSet, prevIdSet) // O(n)
const removed = prev.filter((o) => removedIdSet.has(o.id)) // O(n)
const added = current.filter((o) => addedIdSet.has(o.id)) // O(m)
console.log("removed", removed)
console.log("added", added)
// Total complexity O(constantA * n + constantB * m) ~ O(n + m)
You could use filter. Something like this for items to remove:
arr1.filter(function(i) {return arr2.indexOf(i) < 0;});
And swap the arrays for items to add. For example:
let arr1 = [{id: '1'},{id: '2'},{id: '3'},{id: '4'},{id: '5'}];
let arr2 = [{id: '1'},{id: '5'},{id: '6'},{id: '7'},{id: '8'}];
arr1 = arr1.map((obj) => obj.id);
arr2 = arr2.map((obj) => obj.id);
console.log("Remove these IDs: ", arr1.filter(function(i) {return arr2.indexOf(i) < 0;}));
console.log("Add these IDs: ", arr2.filter(function(i) {return arr1.indexOf(i) < 0;}));
You you only needs the ids: You could extract the ids with flatMap and than filter with every for not incudes.
let old = [{id: '1'},{id: '2'},{id: '3'},{id: '4'},{id: '5'}];
let act = [{id: '1'},{id: '5'},{id: '6'},{id: '7'},{id: '8'}];
let oldIds = old.flatMap(el => el.id);
let actIds = act.flatMap(el => el.id);
let add = actIds.filter(id => oldIds.every(old=> !old.includes(id)));
let del = oldIds.filter(id => actIds.every(act=> !act.includes(id)));
console.log('Add: ', add);
console.log('Del: ', del);

Loop through two arrays and get a desired output using javascript

I have two arrays as follows
const arr1 = ['john', 'robot'];
const arr2 = [{name : 'john'}, {name : 'kevin'}, {name : 'james}];
My desired output is to manipulate array arr1 such that if value of arr1 is not present as a name property in arr2 it should get removed from arr1;
So in the above example after the operation, the output should be
console.log(arr1) // ['john']
as robot is not present in arr2, it gets removed.
Likewise if I have following set of arrays
const names = ['sachin', 'sehwag'];
const players = [{name : 'dhoni'}, {name : 'dravid'}, {name : 'ganguly'} , {name : 'laxman}];
names array should be manipulated to
console.log(names) // []
as both sachin and sehwag is not present in players array
Please help.Thanks in advance.
If you can use es6, a Set is good for this. They only store unique values and are efficient at lookup. They give constant time lookups, which looping through an array doesn't:
const arr1 = ['john', 'robot'];
const arr2 = [{name : 'john'}, {name : 'kevin'}, {name : 'james'}];
// create a set with names
let testSet = arr2.reduce((set, obj) => set.add(obj.name), new Set)
let filtered = arr1.filter(item => testSet.has(item))
console.log(filtered)
You can first grab all the names from array of objects arr2, and then filter the arr1 based on names list..
var arr1 = ["john", "robot"];
var arr2 = [{ name: "john" }, { name: "kevin" }, { name: "james" }];
var names = arr2.map(ob => ob.name);
console.log(arr1.filter(name => names.includes(name)));
var arr1 = ["sachin", "sehwag"];
var arr2 = [
{ name: "dhoni" },
{ name: "dravid" },
{ name: "ganguly" },
{ name: "laxman" }
];
var names = arr2.map(ob => ob.name);
console.log(arr1.filter(name => names.includes(name)));
const arr1 = ['john', 'robot'];
const arr2 = [{name : 'john'}, {name : 'kevin'}, {name : 'james'}];
var newArray= [];
for(var i in arr2) {
var content = arr2[i]['name'];
if(arr1.indexOf(content) > -1){
newArray.push(content);
}
}
console.log(newArray);
Hope it helps you!
This sounds like a job for [].filter:
function doTheThingYouWantItToDo(a, b) {
return a.filter(function(elem) {
for (var i = 0; i < b.length; i+=1) {
if (b[i].name == elem) {
return true;
}
}
return false;
});
}

TypeScript - Take object out of array based on attribute value

My array looks like this:
array = [object {id: 1, value: "itemname"}, object {id: 2, value: "itemname"}, ...]
all my objects have the same attibutes, but with different values.
Is there an easy way I can use a WHERE statement for that array?
Take the object where object.id = var
or do I just need to loop over the entire array and check every item? My array has over a 100 entries, so I wanted to know if there was a more efficient way
Use Array.find:
let array = [
{ id: 1, value: "itemname" },
{ id: 2, value: "itemname" }
];
let item1 = array.find(i => i.id === 1);
Array.find at MDN: https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Array/find
I'd use filter or reduce:
let array = [
{ id: 1, value: "itemname" },
{ id: 2, value: "itemname" }
];
let item1 = array.filter(item => item.id === 1)[0];
let item2 = array.reduce((prev, current) => prev || current.id === 1 ? current : null);
console.log(item1); // Object {id: 1, value: "itemname"}
console.log(item2); // Object {id: 1, value: "itemname"}
(code in playground)
If you care about iterating over the entire array then use some:
let item;
array.some(i => {
if (i.id === 1) {
item = i;
return true;
}
return false;
});
(code in playground)
You can search a certain value in array of objects using TypeScript dynamically if you need to search the value from all fields of the object without specifying column
var searchText = 'first';
let items = [
{ id: 1, name: "first", grade: "A" },
{ id: 2, name: "second", grade: "B" }
];
This below code will search for the value
var result = items.filter(item =>
Object.keys(item).some(k => item[k] != null &&
item[k].toString().toLowerCase()
.includes(searchText.toLowerCase()))
);
Same approach can be used to make a Search Filter Pipe in angularjs 4 using TypeScript
I had to declare the type to get it to work in typescript:
let someId = 1
array.find((i: { id: string; }) => i.id === someId)
You'll have to loop over the array, but if you make a hashmap to link each id to an index and save that, you only have to do it once, so you can reference any objeft after that directly:
var idReference = myArray.reduce(function( map, record, index ) {
map[ record.id ] = index;
return map;
}, {});
var objectWithId5 = myArray[ idReference["5"] ];
This does assume all ids are unique though.

How to merge two dictionaries in javascript

I have two arrays of objects:
Array1:
var myArr1 = [];
myArr1["1"]={any:1,some:1};
myArr1["2"]={any:2,some:2};
myArr1["3"]={any:3,some:3};
Array2:
var myArr2 = [];
myArr2["1"]={other:1};
myArr2["2"]={other:2};
And I want them to be merged by their keys into a new Attribute, so the result will be:
[
{any:1,some:1,myNewAttribute:{other:1}},
{any:2,some:2,myNewAttribute:{other:2}},
{any:3,some:3,myNewAttribute:{other:3}}
]
I tried to achieve it with lodash's _.merge() but I failed miserably. _.merge only adds the second array after the first, but does not match their keys / ids.
You could map the second array to a new property and merge later.
With lodash
var data1 = [{ any: 1, some: 1 }, { any: 2, some: 2 }, { any: 3, some: 3 }],
data2 = [{ other: 1 }, { other: 2 }, { other: 3 }];
console.log(_.merge(data1, _.map(data2, x => ({ myNewAttribute: x }))));
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.15.0/lodash.min.js"></script>
With ES6, without lodash
var data1 = [{ any: 1, some: 1 }, { any: 2, some: 2 }, { any: 3, some: 3 }],
data2 = [{ other: 1 }, { other: 2 }, { other: 3 }];
console.log(data1.map((a, i) => Object.assign({}, a, { myNewAttribute: data2[i] })));
You don't need lodash:
myArr1.map((e1, idx) => Object.assign({}, e1, {myNewAttribute: myArr2[idx]}))
You could get fancy and write a little function called map2, which takes two arrays, and invokes a callback with the two elements:
function map2(a1, a2, fn) {
return a1.map((elt, idx) => fn(elt, a2[idx]);
}
Now you can write the solution as
map2(myArr1, myArr2, (e1, e2) => Object.assign({}, e1, {myNewAttribute: e2}))
From the perspective of program design, what we are doing here is "separating concerns". The first concern is the abstract operation of looping over two arrays in parallel and doing something with each pair of elements. That is what is represented by map2. The second concern is the specific way you want to combine the elements. That is what is represented by the function we are passing to map2. This could be made clearer and somewhat self-documenting by writing it separately:
function combineObjects(e1, e2) {
return Object.assign({}, e1, {myNewAttribute: e2});
}
map2(myArr1, myArr2, combineObjects);
Of course, in the real world, you'd want to handle the case where the two arrays were of different length, pass an index to the callback as a third parameter for use if necessary, support a third thisArg-type parameter analogous to map, etc.
You can do like this:
var first = [{any:1,some:1},{any:2,some:2},{any:3,some:3}];
var second = [{other:1},{other:2},{other:3}];
for(var i = 0; i < first.length; i++){
if(first[i] && second[i]){
first[i]['mycustomitem'] = second[i];
}
}
console.log(first);
In order to prove, what I did comment 30 minutes ago -
How to merge two dictionaries in javascript -
there is a possible reduce approach ...
... firstly provided as lodash based example ...
var
myArr1 = [
{any: 1, some: 1},
{any: 2, some: 2},
{any: 3, some: 3}
],
myArr2 = [
{other: 1},
{other: 2}
],
mergedObjectList = _.reduce(myArr1, function (collector, item_1, idx) {
var
item_2 = collector[idx],
merger = _.assign({}, item_1, item_2);
// or whatever one wants to do to `merger` with `myNewAttribute`
collector[idx] = merger;
return collector;
}, _.clone(myArr2));
console.log("myArr1 : ", myArr1);
console.log("myArr2 : ", myArr2);
console.log("mergedObjectList : ", mergedObjectList);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.15.0/lodash.min.js"></script>
... and secondly as language core only based example ...
var
myArr1 = [
{any: 1, some: 1},
{any: 2, some: 2},
{any: 3, some: 3}
],
myArr2 = [
{other: 1},
{other: 2}
],
mergedObjectList = myArr1.reduce(function (collector, item_1, idx) {
var
item_2 = collector[idx],
merger = Object.assign({}, item_1, item_2);
// or whatever one wants to do to `merger` with `myNewAttribute`
collector[idx] = merger;
return collector;
}, Array.from(myArr2));
console.log("myArr1 : ", myArr1);
console.log("myArr2 : ", myArr2);
console.log("mergedObjectList : ", mergedObjectList);
Try this function:
function mergeDictionary(_dctn1,_dctn2)
{
var newDict = [];
for(var i in _dctn1)
{
newDict[i] = _dctn1[i];
}
for(var j in _dctn2)
{
if(newDict[j] == undefined)
{
newDict[j] = _dctn2[j];
}
else
{
for(var k in _dctn2[j])
{
newDict[j][k] = _dctn2[j][k];
}
}
}
return newDict;
}
var myArr1 = [];
myArr1["1"]={any:1,some:1};
myArr1["2"]={any:2,some:2};
myArr1["3"]={any:3,some:3};
var myArr2 = [];
myArr2["1"]={other:1};
myArr2["2"]={other:2};
console.log(mergeDictionary(myArr1, myArr2));

How can I get a unique array based on object property using underscore

I have an array of objects and I want to get a new array from it that is unique based only on a single property, is there a simple way to achieve this?
Eg.
[ { id: 1, name: 'bob' }, { id: 1, name: 'bill' }, { id: 1, name: 'bill' } ]
Would result in 2 objects with name = bill removed once.
Use the uniq function
var destArray = _.uniq(sourceArray, function(x){
return x.name;
});
or single-line version
var destArray = _.uniq(sourceArray, x => x.name);
From the docs:
Produces a duplicate-free version of the array, using === to test object equality. If you know in advance that the array is sorted, passing true for isSorted will run a much faster algorithm. If you want to compute unique items based on a transformation, pass an iterator function.
In the above example, the function uses the objects name in order to determine uniqueness.
If you prefer to do things yourself without Lodash, and without getting verbose, try this uniq filter with optional uniq by property:
const uniqFilterAccordingToProp = function (prop) {
if (prop)
return (ele, i, arr) => arr.map(ele => ele[prop]).indexOf(ele[prop]) === i
else
return (ele, i, arr) => arr.indexOf(ele) === i
}
Then, use it like this:
const obj = [ { id: 1, name: 'bob' }, { id: 1, name: 'bill' }, { id: 1, name: 'bill' } ]
obj.filter(uniqFilterAccordingToProp('abc'))
Or for plain arrays, just omit the parameter, while remembering to invoke:
[1,1,2].filter(uniqFilterAccordingToProp())
If you want to check all the properties then
lodash 4 comes with _.uniqWith(sourceArray, _.isEqual)
A better and quick approach
var table = [
{
a:1,
b:2
},
{
a:2,
b:3
},
{
a:1,
b:4
}
];
let result = [...new Set(table.map(item => item.a))];
document.write(JSON.stringify(result));
Found here
You can use the _.uniqBy function
var array = [ { id: 1, name: 'bob' }, { id: 2, name: 'bill' }, { id: 1, name: 'bill' },{ id: 2, name: 'bill' } ];
var filteredArray = _.uniqBy(array,function(x){ return x.id && x.name;});
console.log(filteredArray)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.5/lodash.js"></script>
In the above example, filtering is based on the uniqueness of combination of properties id & name.
if you have multiple properties for an object.
then to find unique array of objects based on specific properties, you could follow this method of combining properties inside _.uniqBy() method.
I was looking for a solution which didn't require a library, and put this together, so I thought I'd add it here. It may not be ideal, or working in all situations, but it's doing what I require, so could potentially help someone else:
const uniqueBy = (items, reducer, dupeCheck = [], currentResults = []) => {
if (!items || items.length === 0) return currentResults;
const thisValue = reducer(items[0]);
const resultsToPass = dupeCheck.indexOf(thisValue) === -1 ?
[...currentResults, items[0]] : currentResults;
return uniqueBy(
items.slice(1),
reducer,
[...dupeCheck, thisValue],
resultsToPass,
);
}
const testData = [
{text: 'hello', image: 'yes'},
{text: 'he'},
{text: 'hello'},
{text: 'hell'},
{text: 'hello'},
{text: 'hellop'},
];
const results = uniqueBy(
testData,
item => {
return item.text
},
)
console.dir(results)
In case you need pure JavaScript solution:
var uniqueProperties = {};
var notUniqueArray = [ { id: 1, name: 'bob' }, { id: 1, name: 'bill' }, { id: 1, name: 'bill' } ];
for(var object in notUniqueArray){
uniqueProperties[notUniqueArray[object]['name']] = notUniqueArray[object]['id'];
}
var uniqiueArray = [];
for(var uniqueName in uniqueProperties){
uniqiueArray.push(
{id:uniqueProperties[uniqueName],name:uniqueName});
}
//uniqiueArray
unique array by id property with ES6:
arr.filter((a, i) => arr.findIndex(b => b.id === a.id) === i); // unique by id
replace b.id === a.id with the relevant comparison for your case

Categories

Resources