How to get the difference between two arrays of objects in JavaScript - javascript

I have two result sets like this:
// Result 1
[
{ value: "0", display: "Jamsheer" },
{ value: "1", display: "Muhammed" },
{ value: "2", display: "Ravi" },
{ value: "3", display: "Ajmal" },
{ value: "4", display: "Ryan" }
]
// Result 2
[
{ value: "0", display: "Jamsheer" },
{ value: "1", display: "Muhammed" },
{ value: "2", display: "Ravi" },
{ value: "3", display: "Ajmal" },
]
The final result I need is the difference between these arrays ā€“ the final result should be like this:
[{ value: "4", display: "Ryan" }]
Is it possible to do something like this in JavaScript?

Using only native JS, something like this will work:
const a = [{ value:"0", display:"Jamsheer" }, { value:"1", display:"Muhammed" }, { value:"2", display:"Ravi" }, { value:"3", display:"Ajmal" }, { value:"4", display:"Ryan" }];
const b = [{ value:"0", display:"Jamsheer", $$hashKey:"008" }, { value:"1", display:"Muhammed", $$hashKey:"009" }, { value:"2", display:"Ravi", $$hashKey:"00A" }, { value:"3", display:"Ajmal", $$hashKey:"00B" }];
// A comparer used to determine if two entries are equal.
const isSameUser = (a, b) => a.value === b.value && a.display === b.display;
// Get items that only occur in the left array,
// using the compareFunction to determine equality.
const onlyInLeft = (left, right, compareFunction) =>
left.filter(leftValue =>
!right.some(rightValue =>
compareFunction(leftValue, rightValue)));
const onlyInA = onlyInLeft(a, b, isSameUser);
const onlyInB = onlyInLeft(b, a, isSameUser);
const result = [...onlyInA, ...onlyInB];
console.log(result);

For those who like one-liner solutions in ES6, something like this:
const arrayOne = [
{ value: "4a55eff3-1e0d-4a81-9105-3ddd7521d642", display: "Jamsheer" },
{ value: "644838b3-604d-4899-8b78-09e4799f586f", display: "Muhammed" },
{ value: "b6ee537a-375c-45bd-b9d4-4dd84a75041d", display: "Ravi" },
{ value: "e97339e1-939d-47ab-974c-1b68c9cfb536", display: "Ajmal" },
{ value: "a63a6f77-c637-454e-abf2-dfb9b543af6c", display: "Ryan" },
];
const arrayTwo = [
{ value: "4a55eff3-1e0d-4a81-9105-3ddd7521d642", display: "Jamsheer"},
{ value: "644838b3-604d-4899-8b78-09e4799f586f", display: "Muhammed"},
{ value: "b6ee537a-375c-45bd-b9d4-4dd84a75041d", display: "Ravi"},
{ value: "e97339e1-939d-47ab-974c-1b68c9cfb536", display: "Ajmal"},
];
const results = arrayOne.filter(({ value: id1 }) => !arrayTwo.some(({ value: id2 }) => id2 === id1));
console.log(results);

You could use Array.prototype.filter() in combination with Array.prototype.some().
Here is an example (assuming your arrays are stored in the variables result1 and result2):
//Find values that are in result1 but not in result2
var uniqueResultOne = result1.filter(function(obj) {
return !result2.some(function(obj2) {
return obj.value == obj2.value;
});
});
//Find values that are in result2 but not in result1
var uniqueResultTwo = result2.filter(function(obj) {
return !result1.some(function(obj2) {
return obj.value == obj2.value;
});
});
//Combine the two arrays of unique entries
var result = uniqueResultOne.concat(uniqueResultTwo);

import differenceBy from 'lodash/differenceBy'
const myDifferences = differenceBy(Result1, Result2, 'value')
This will return the difference between two arrays of objects, using the key value to compare them. Note two things with the same value will not be returned, as the other keys are ignored.
This is a part of lodash.

I take a slightly more general-purpose approach, although similar in ideas to the approaches of both #Cerbrus and #Kasper Moerch. I create a function that accepts a predicate to determine if two objects are equal (here we ignore the $$hashKey property, but it could be anything) and return a function which calculates the symmetric difference of two lists based on that predicate:
a = [{ value:"4a55eff3-1e0d-4a81-9105-3ddd7521d642", display:"Jamsheer"}, { value:"644838b3-604d-4899-8b78-09e4799f586f", display:"Muhammed"}, { value:"b6ee537a-375c-45bd-b9d4-4dd84a75041d", display:"Ravi"}, { value:"e97339e1-939d-47ab-974c-1b68c9cfb536", display:"Ajmal"}, { value:"a63a6f77-c637-454e-abf2-dfb9b543af6c", display:"Ryan"}]
b = [{ value:"4a55eff3-1e0d-4a81-9105-3ddd7521d642", display:"Jamsheer", $$hashKey:"008"}, { value:"644838b3-604d-4899-8b78-09e4799f586f", display:"Muhammed", $$hashKey:"009"}, { value:"b6ee537a-375c-45bd-b9d4-4dd84a75041d", display:"Ravi", $$hashKey:"00A"}, { value:"e97339e1-939d-47ab-974c-1b68c9cfb536", display:"Ajmal", $$hashKey:"00B"}]
var makeSymmDiffFunc = (function() {
var contains = function(pred, a, list) {
var idx = -1, len = list.length;
while (++idx < len) {if (pred(a, list[idx])) {return true;}}
return false;
};
var complement = function(pred, a, b) {
return a.filter(function(elem) {return !contains(pred, elem, b);});
};
return function(pred) {
return function(a, b) {
return complement(pred, a, b).concat(complement(pred, b, a));
};
};
}());
var myDiff = makeSymmDiffFunc(function(x, y) {
return x.value === y.value && x.display === y.display;
});
var result = myDiff(a, b); //=> {value="a63a6f77-c637-454e-abf2-dfb9b543af6c", display="Ryan"}
It has one minor advantage over Cerebrus's approach (as does Kasper Moerch's approach) in that it escapes early; if it finds a match, it doesn't bother checking the rest of the list. If I had a curry function handy, I would do this a little differently, but this works fine.
Explanation
A comment asked for a more detailed explanation for beginners. Here's an attempt.
We pass the following function to makeSymmDiffFunc:
function(x, y) {
return x.value === y.value && x.display === y.display;
}
This function is how we decide that two objects are equal. Like all functions that return true or false, it can be called a "predicate function", but that's just terminology. The main point is that makeSymmDiffFunc is configured with a function that accepts two objects and returns true if we consider them equal, false if we don't.
Using that, makeSymmDiffFunc (read "make symmetric difference function") returns us a new function:
return function(a, b) {
return complement(pred, a, b).concat(complement(pred, b, a));
};
This is the function we will actually use. We pass it two lists and it finds the elements in the first not in the second, then those in the second not in the first and combine these two lists.
Looking over it again, though, I could definitely have taken a cue from your code and simplified the main function quite a bit by using some:
var makeSymmDiffFunc = (function() {
var complement = function(pred, a, b) {
return a.filter(function(x) {
return !b.some(function(y) {return pred(x, y);});
});
};
return function(pred) {
return function(a, b) {
return complement(pred, a, b).concat(complement(pred, b, a));
};
};
}());
complement uses the predicate and returns the elements of its first list not in its second. This is simpler than my first pass with a separate contains function.
Finally, the main function is wrapped in an immediately invoked function expression (IIFE) to keep the internal complement function out of the global scope.
Update, a few years later
Now that ES2015 has become pretty well ubiquitous, I would suggest the same technique, with a lot less boilerplate:
const diffBy = (pred) => (a, b) => a.filter(x => !b.some(y => pred(x, y)))
const makeSymmDiffFunc = (pred) => (a, b) => diffBy(pred)(a, b).concat(diffBy(pred)(b, a))
const myDiff = makeSymmDiffFunc((x, y) => x.value === y.value && x.display === y.display)
const result = myDiff(a, b)
//=> {value="a63a6f77-c637-454e-abf2-dfb9b543af6c", display="Ryan"}

In addition, say two object array with different key value
// Array Object 1
const arrayObjOne = [
{ userId: "1", display: "Jamsheer" },
{ userId: "2", display: "Muhammed" },
{ userId: "3", display: "Ravi" },
{ userId: "4", display: "Ajmal" },
{ userId: "5", display: "Ryan" }
]
// Array Object 2
const arrayObjTwo =[
{ empId: "1", display: "Jamsheer", designation:"Jr. Officer" },
{ empId: "2", display: "Muhammed", designation:"Jr. Officer" },
{ empId: "3", display: "Ravi", designation:"Sr. Officer" },
{ empId: "4", display: "Ajmal", designation:"Ast. Manager" },
]
You can use filter in es5 or native js to substract two array object.
//Find data that are in arrayObjOne but not in arrayObjTwo
var uniqueResultArrayObjOne = arrayObjOne.filter(function(objOne) {
return !arrayObjTwo.some(function(objTwo) {
return objOne.userId == objTwo.empId;
});
});
In ES6 you can use Arrow function with Object destructuring of ES6.
const ResultArrayObjOne = arrayObjOne.filter(({ userId: userId }) => !arrayObjTwo.some(({ empId: empId }) => empId === userId));
console.log(ResultArrayObjOne);

You can create an object with keys as the unique value corresponding for each object in array and then filter each array based on existence of the key in other's object. It reduces the complexity of the operation.
ES6
let a = [{ value:"4a55eff3-1e0d-4a81-9105-3ddd7521d642", display:"Jamsheer"}, { value:"644838b3-604d-4899-8b78-09e4799f586f", display:"Muhammed"}, { value:"b6ee537a-375c-45bd-b9d4-4dd84a75041d", display:"Ravi"}, { value:"e97339e1-939d-47ab-974c-1b68c9cfb536", display:"Ajmal"}, { value:"a63a6f77-c637-454e-abf2-dfb9b543af6c", display:"Ryan"}];
let b = [{ value:"4a55eff3-1e0d-4a81-9105-3ddd7521d642", display:"Jamsheer", $$hashKey:"008"}, { value:"644838b3-604d-4899-8b78-09e4799f586f", display:"Muhammed", $$hashKey:"009"}, { value:"b6ee537a-375c-45bd-b9d4-4dd84a75041d", display:"Ravi", $$hashKey:"00A"}, { value:"e97339e1-939d-47ab-974c-1b68c9cfb536", display:"Ajmal", $$hashKey:"00B"}];
let valuesA = a.reduce((a,{value}) => Object.assign(a, {[value]:value}), {});
let valuesB = b.reduce((a,{value}) => Object.assign(a, {[value]:value}), {});
let result = [...a.filter(({value}) => !valuesB[value]), ...b.filter(({value}) => !valuesA[value])];
console.log(result);
ES5
var a = [{ value:"4a55eff3-1e0d-4a81-9105-3ddd7521d642", display:"Jamsheer"}, { value:"644838b3-604d-4899-8b78-09e4799f586f", display:"Muhammed"}, { value:"b6ee537a-375c-45bd-b9d4-4dd84a75041d", display:"Ravi"}, { value:"e97339e1-939d-47ab-974c-1b68c9cfb536", display:"Ajmal"}, { value:"a63a6f77-c637-454e-abf2-dfb9b543af6c", display:"Ryan"}];
var b = [{ value:"4a55eff3-1e0d-4a81-9105-3ddd7521d642", display:"Jamsheer", $$hashKey:"008"}, { value:"644838b3-604d-4899-8b78-09e4799f586f", display:"Muhammed", $$hashKey:"009"}, { value:"b6ee537a-375c-45bd-b9d4-4dd84a75041d", display:"Ravi", $$hashKey:"00A"}, { value:"e97339e1-939d-47ab-974c-1b68c9cfb536", display:"Ajmal", $$hashKey:"00B"}];
var valuesA = a.reduce(function(a,c){a[c.value] = c.value; return a; }, {});
var valuesB = b.reduce(function(a,c){a[c.value] = c.value; return a; }, {});
var result = a.filter(function(c){ return !valuesB[c.value]}).concat(b.filter(function(c){ return !valuesA[c.value]}));
console.log(result);

I found this solution using filter and some.
resultFilter = (firstArray, secondArray) => {
return firstArray.filter(firstArrayItem =>
!secondArray.some(
secondArrayItem => firstArrayItem._user === secondArrayItem._user
)
);
};

I think the #Cerbrus solution is spot on. I have implemented the same solution but extracted the repeated code into it's own function (DRY).
function filterByDifference(array1, array2, compareField) {
var onlyInA = differenceInFirstArray(array1, array2, compareField);
var onlyInb = differenceInFirstArray(array2, array1, compareField);
return onlyInA.concat(onlyInb);
}
function differenceInFirstArray(array1, array2, compareField) {
return array1.filter(function (current) {
return array2.filter(function (current_b) {
return current_b[compareField] === current[compareField];
}).length == 0;
});
}

you can do diff a on b and diff b on a, then merge both results
let a = [
{ value: "0", display: "Jamsheer" },
{ value: "1", display: "Muhammed" },
{ value: "2", display: "Ravi" },
{ value: "3", display: "Ajmal" },
{ value: "4", display: "Ryan" }
]
let b = [
{ value: "0", display: "Jamsheer" },
{ value: "1", display: "Muhammed" },
{ value: "2", display: "Ravi" },
{ value: "3", display: "Ajmal" }
]
// b diff a
let resultA = b.filter(elm => !a.map(elm => JSON.stringify(elm)).includes(JSON.stringify(elm)));
// a diff b
let resultB = a.filter(elm => !b.map(elm => JSON.stringify(elm)).includes(JSON.stringify(elm)));
// show merge
console.log([...resultA, ...resultB]);

let obj1 =[
{ id: 1, submenu_name: 'login' },
{ id: 2, submenu_name: 'Profile',},
{ id: 3, submenu_name: 'password', },
{ id: 4, submenu_name: 'reset',}
] ;
let obj2 =[
{ id: 2},
{ id: 3 },
] ;
// Need Similar obj
const result1 = obj1.filter(function(o1){
return obj2.some(function(o2){
return o1.id == o2.id; // id is unnique both array object
});
});
console.log(result1);
// Need differnt obj
const result2 = obj1.filter(function(o1){
return !obj2.some(function(o2){ // for diffrent we use NOT (!) befor obj2 here
return o1.id == o2.id; // id is unnique both array object
});
});
console.log(result2);

JavaScript has Maps, that provide O(1) insertion and lookup time. Therefore this can be solved in O(n) (and not O(nĀ²) as all the other answers do). For that, it is necessary to generate a unique primitive (string / number) key for each object. One could JSON.stringify, but that's quite error prone as the order of elements could influence equality:
JSON.stringify({ a: 1, b: 2 }) !== JSON.stringify({ b: 2, a: 1 })
Therefore, I'd take a delimiter that does not appear in any of the values and compose a string manually:
const toHash = value => value.value + "#" + value.display;
Then a Map gets created. When an element exists already in the Map, it gets removed, otherwise it gets added. Therefore only the elements that are included odd times (meaning only once) remain. This will only work if the elements are unique in each array:
const entries = new Map();
for(const el of [...firstArray, ...secondArray]) {
const key = toHash(el);
if(entries.has(key)) {
entries.delete(key);
} else {
entries.set(key, el);
}
}
const result = [...entries.values()];
const firstArray = [
{ value: "0", display: "Jamsheer" },
{ value: "1", display: "Muhammed" },
{ value: "2", display: "Ravi" },
{ value: "3", display: "Ajmal" },
{ value: "4", display: "Ryan" }
]
const secondArray = [
{ value: "0", display: "Jamsheer" },
{ value: "1", display: "Muhammed" },
{ value: "2", display: "Ravi" },
{ value: "3", display: "Ajmal" },
];
const toHash = value => value.value + "#" + value.display;
const entries = new Map();
for(const el of [...firstArray, ...secondArray]) {
const key = toHash(el);
if(entries.has(key)) {
entries.delete(key);
} else {
entries.set(key, el);
}
}
const result = [...entries.values()];
console.log(result);

Most of the observed code does not examine the whole object and only compares the value of a particular method. This solution is the same, except that you can specify that method yourself.
Here is an example:
const arr1 = [
{
id: 1,
name: "Tom",
scores: {
math: 80,
science: 100
}
},
{
id: 2,
name: "John",
scores: {
math: 50,
science: 70
}
}
];
const arr2 = [
{
id: 1,
name: "Tom",
scores: {
math: 80,
science: 70
}
}
];
function getDifference(array1, array2, attr) {
return array1.filter(object1 => {
return !array2.some(object2 => {
return eval("object1." + attr + " == object2." + attr);
});
});
}
// šŸ‘‡ļø [{id: 2, name: 'John'...
console.log(getDifference(arr1, arr2, "id"));
// šŸ‘‡ļø [{id: 2, name: 'John'...
console.log(getDifference(arr1, arr2, "scores.math"));
// šŸ‘‡ļø [{id: 1, name: 'Tom'...
console.log(getDifference(arr1, arr2, "scores.science"));

Most of answers here are rather complex, but isn't logic behind this quite simple?
check which array is longer and provide it as first parameter (if length is equal, parameters order doesnt matter)
Iterate over array1.
For current iteration element of array1 check if it is present in array2
If it is NOT present, than
Push it to 'difference' array
const getArraysDifference = (longerArray, array2) => {
const difference = [];
longerArray.forEach(el1 => { /*1*/
el1IsPresentInArr2 = array2.some(el2 => el2.value === el1.value); /*2*/
if (!el1IsPresentInArr2) { /*3*/
difference.push(el1); /*4*/
}
});
return difference;
}
O(n^2) complexity.

I prefer map object when it comes to big arrays.
// create tow arrays
array1 = Array.from({length: 400},() => ({value:Math.floor(Math.random() * 4000)}))
array2 = Array.from({length: 400},() => ({value:Math.floor(Math.random() * 4000)}))
// calc diff with some function
console.time('diff with some');
results = array2.filter(({ value: id1 }) => array1.some(({ value: id2 }) => id2 === id1));
console.log('diff results ',results.length)
console.timeEnd('diff with some');
// calc diff with map object
console.time('diff with map');
array1Map = {};
for(const item1 of array1){
array1Map[item1.value] = true;
}
results = array2.filter(({ value: id2 }) => array1Map[id2]);
console.log('map results ',results.length)
console.timeEnd('diff with map');

Having:
const array = [{id:3, name:'xx'} , {id:7, name:'yy'}, {id:9, name:'zz'}];
const array2 =[3,4,5];//These are a group of ids
I made a function that removes the objects from array that matches de ids within array2, like this:
export const aFilter = (array, array2) => {
array2.forEach(element => {
array = array.filter(item=> item.id !== element);
});
return array;
}
After calling the function we should have the array with no object wit id = 3
const rta = aFilter(array, array2);
//rta should be = [{id:7, name:'yy'}, {id:9, name:'zz'}];
It worked for me, and was pretty easy

I've made a generalized diff that compare 2 objects of any kind and can run a modification handler
gist.github.com/bortunac "diff.js"
an ex of using :
old_obj={a:1,b:2,c:[1,2]}
now_obj={a:2 , c:[1,3,5],d:55}
so property a is modified, b is deleted, c modified, d is added
var handler=function(type,pointer){
console.log(type,pointer,this.old.point(pointer)," | ",this.now.point(pointer));
}
now use like
df=new diff();
df.analize(now_obj,old_obj);
df.react(handler);
the console will show
mdf ["a"] 1 | 2
mdf ["c", "1"] 2 | 3
add ["c", "2"] undefined | 5
add ["d"] undefined | 55
del ["b"] 2 | undefined

Most generic and simple way:
findObject(listOfObjects, objectToSearch) {
let found = false, matchingKeys = 0;
for(let object of listOfObjects) {
found = false;
matchingKeys = 0;
for(let key of Object.keys(object)) {
if(object[key]==objectToSearch[key]) matchingKeys++;
}
if(matchingKeys==Object.keys(object).length) {
found = true;
break;
}
}
return found;
}
get_removed_list_of_objects(old_array, new_array) {
// console.log('old:',old_array);
// console.log('new:',new_array);
let foundList = [];
for(let object of old_array) {
if(!this.findObject(new_array, object)) foundList.push(object);
}
return foundList;
}
get_added_list_of_objects(old_array, new_array) {
let foundList = [];
for(let object of new_array) {
if(!this.findObject(old_array, object)) foundList.push(object);
}
return foundList;
}

I came across this question while searching for a way to pick out the first item in one array that does not match any of the values in another array and managed to sort it out eventually with array.find() and array.filter() like this
var carList= ['mercedes', 'lamborghini', 'bmw', 'honda', 'chrysler'];
var declinedOptions = ['mercedes', 'lamborghini'];
const nextOption = carList.find(car=>{
const duplicate = declinedOptions.filter(declined=> {
return declined === car
})
console.log('duplicate:',duplicate) //should list out each declined option
if(duplicate.length === 0){//if theres no duplicate, thats the nextOption
return car
}
})
console.log('nextOption:', nextOption);
//expected outputs
//duplicate: mercedes
//duplicate: lamborghini
//duplicate: []
//nextOption: bmw
if you need to keep fetching an updated list before cross-checking for the next best option this should work well enough :)

#atheane response : fork :
const newList = [
{ value: "4a55eff3-1e0d-4a81-9105-3ddd7521d642", display: "Jamsheer" },
{ value: "644838b3-604d-4899-8b78-09e4799f586f", display: "Muhammed" },
{ value: "b6ee537a-375c-45bd-b9d4-4dd84a75041d", display: "Ravi2" },
{ value: "a63a6f77-c637-454e-abf2-dfb9b543af6c", display: "Ryan" },
];
const oldList = [
{ value: "4a55eff3-1e0d-4a81-9105-3ddd7521d642", display: "Jamsheer"},
{ value: "644838b3-604d-4899-8b78-09e4799f586f", display: "Muhammed"},
{ value: "b6ee537a-375c-45bd-b9d4-4dd84a75041d", display: "Ravi"},
{ value: "e97339e1-939d-47ab-974c-1b68c9cfb536", display: "Ajmal"},
];
const resultsAdd = newList.filter(({ value: id1 }) => !oldList.some(({ value: id2 }) => id2 === id1));
const resultsRemove = oldList.filter(({ value: id1 }) => !newList.some(({ value: id2 }) => id2 === id1));
const resultsUpdate = newList.filter(({ value: id1, ...rest1 }) => oldList.some(({ value: id2, ...rest2 }) => id2 === id1 && JSON.stringify(rest1) !== JSON.stringify(rest2)));
console.log("added", resultsAdd);
console.log("removed", resultsRemove);
console.log("updated", resultsUpdate);

Most simple way could be use of filter and some together
Please refer below link
DifferenceInTwoArrayOfObjectInSimpleWay

If you are willing to use external libraries, You can use _.difference in underscore.js to achieve this. _.difference returns the values from array that are not present in the other arrays.
_.difference([1,2,3,4,5][1,4,10])
==>[2,3,5]

Related

Sort Array Object in Array Object javascript

how to sort highest value in this data using javascript?
data = [{a: [{num:31}, {num:10}]},{a: [{num:4}, {num:9}]},{a: [{num:5}, {num:9}]}]
Expected
data = [{a: [{num:31}]},{a: [{num:9}]},{a: [{num:9}]}]
I try like this but never happen :)
const data_sort = data.sort((a, b) => {
let abc
if (a.a.length > 0) {
abc = a.a.sort((x, y) => x.a - x.a);
}
return a - b
})
let data = [{a: [{num:31}, {num:10}]},{a: [{num:4}, {num:9}]},{a: [{num:5}, {num:9}]}]
data = data.map(item => ({a:[item.a.sort((a, b) => b.num-a.num)[0]]})).sort((a, b) => b.a[0].num-a.a[0].num)
console.log(data)
Assuming this is the correct syntax, all you need is to map every item in the array to it's largest item, then sort that.
var data = [{
a: [{num:31}, {num:10}]
}, {
a: [{num:4}, {num:9}]
}, {
a: [{num:6}, {num:11}]
}, {
a: [{num:5}, {num:9}]
}];
var result = data.map(function(item) {
return {
a: [item.a.sort(function(a,b) {
return a.num - b.num
}).reverse()[0]]
};
}).sort(function(a, b) {
return a.a[0].num - b.a[0].num
}).reverse();
console.log(result)
.as-console-wrapper {
max-height: 100% !important;
}
Map and Reduce
const data = [{ a: [{ num: 31 }, { num: 10 }] }, { a: [{ num: 4 }, { num: 9 }] }, { a: [{ num: 5 }, { num: 9 }] }];
data.map((value) => {
value.a = [
{
num: value.a.reduce((accumulatedValue, currentValue) => {
return Math.max(accumulatedValue.num, currentValue.num);
}),
},
];
return value;
});
console.log(data)

Issue arranging values in ascending order [duplicate]

This question already has answers here:
Sort Array Elements (string with numbers), natural sort
(8 answers)
Closed 11 months ago.
I am trying to arrange given values in ascending orders
const value = [
{ val: "11-1" },
{ val: "12-1b" },
{ val: "12-1a" },
{ val: "12-700" },
{ val: "12-7" },
{ val: "12-8" },
];
I am using code below to sort this in ascending order:
value.sort((a,b)=>(a.val >b.val)? 1:((b.val>a.val)?-1:0));
The result of this sort is in the order 11-1,12-1a, 12-1b, 12-7, 12-700, 12-8. However, I want the order to be 11-1,12-1a, 12-1b, 12-7, 12-8, 12-700.
How can I achieve that?
If you're only interested of sorting by the value after the hyphen you can achieve it with this code:
const value = [
{val:'12-1'},
{val:'12-700'},
{val:'12-7'},
{val:'12-8'},
];
const sorted = value.sort((a,b) => {
const anum = parseInt(a.val.split('-')[1]);
const bnum = parseInt(b.val.split('-')[1]);
return anum - bnum;
});
console.log(sorted);
updated the answer as your question update here's the solution for this:
const value = [{ val: '11-1' }, { val: '12-1b' }, { val: '12-1a' }, { val: '12-700' }, { val: '12-7' }, { val: '12-8' }];
const sortAlphaNum = (a, b) => a.val.localeCompare(b.val, 'en', { numeric: true });
console.log(value.sort(sortAlphaNum));
You can check the length first and then do the sorting as follow:
const value = [
{ val: "12-1" },
{ val: "12-700" },
{ val: "12-7" },
{ val: "12-8" },
];
const result = value.sort(
(a, b)=> {
if (a.val.length > b.val.length) {
return 1;
}
if (a.val.length < b.val.length) {
return -1;
}
return (a.val >b.val) ? 1 : ((b.val > a.val) ? -1 : 0)
}
);
console.log(result);
little change's to #Christian answer it will sort before and after - value
const value = [{ val: '12-1' }, { val: '12-700' }, { val: '11-7' }, { val: '12-8' }];
const sorted = value.sort((a, b) => {
const anum = parseInt(a.val.replace('-', '.'));
const bnum = parseInt(b.val.replace('-', '.'));
return anum - bnum;
});
console.log(sorted);
If you want to check for different values both before and after the hyphen and include checking for letters, the solution at the end will solve this.
Here's what I did:
Created a regex to split the characters by type:
var regexValueSplit = /(\d+)([a-z]+)?-(\d+)([a-z]+)?/gi;
Created a comparison function to take numbers and letters into account:
function compareTypes(alpha, bravo) {
if (!isNaN(alpha) && !isNaN(bravo)) {
return parseInt(alpha) - parseInt(bravo);
}
return alpha > bravo;
}
Split the values based on regexValueSplit:
value.sort((a, b) => {
let valuesA = a.val.split(regexValueSplit);
let valuesB = b.val.split(regexValueSplit);
This produces results as follows (example string "12-1a"):
[
"",
"12",
null,
"1",
"a",
""
]
Then, since all the split arrays should have the same length, compare each value in a for loop:
for (let i = 0; i < valuesA.length; i++) {
if (valuesA[i] !== valuesB[i]) {
return compareTypes(valuesA[i], valuesB[i]);
}
}
// Return 0 if all values are equal
return 0;
const value = [{
val: "11-1"
},
{
val: "12-1b"
},
{
val: "12-1a"
},
{
val: "12-700"
},
{
val: "12-7"
},
{
val: "12-8"
},
];
var regexValueSplit = /(\d+)([a-z]+)?-(\d+)([a-z]+)?/gi;
function compareTypes(alpha, bravo) {
if (!isNaN(alpha) && !isNaN(bravo)) {
return parseInt(alpha) - parseInt(bravo);
}
return alpha > bravo;
}
value.sort((a, b) => {
let valuesA = a.val.split(regexValueSplit);
let valuesB = b.val.split(regexValueSplit);
for (let i = 0; i < valuesA.length; i++) {
if (valuesA[i] !== valuesB[i]) {
return compareTypes(valuesA[i], valuesB[i]);
}
}
return 0;
});
console.log(JSON.stringify(value, null, 2));
Since you are sorting on string values, try using String.localeCompare for the sorting.
Try sorting on both numeric components of the string.
const arr = [
{val:'12-1'},
{val:'11-900'},
{val:'12-700'},
{val:'12-7'},
{val:'11-1'},
{val:'12-8'},
{val:'11-90'},
];
const sorter = (a, b) => {
const [a1, a2, b1, b2] = (a.val.split(`-`)
.concat(b.val.split(`-`))).map(Number);
return a1 - b1 || a2 - b2; };
console.log(`Unsorted values:\n${
JSON.stringify(arr.map(v => v.val))}`);
console.log(`Sorted values:\n${
JSON.stringify(arr.sort(sorter).map(v => v.val))}`);

Nested grouping with javascript (ES5)

I have an array of objects as following :
[
{"id":1,"lib":"A","categoryID":10,"categoryTitle":"Cat10","moduleID":"2","moduleTitle":"Module 2"},
{"id":2,"lib":"B","categoryID":10,"categoryTitle":"Cat10","moduleID":"2","moduleTitle":"Module 2"},
...
{"id":110,"lib":"XXX","categoryID":90,"categoryTitle":"Cat90","moduleID":"4","moduleTitle":"Module 4"}
]
I want to group this array by (moduleID,moduleTitle) and then by (categoryID,categoryTitle).
This is what I tried :
function groupBy(data, id, text) {
return data.reduce(function (rv, x) {
var el = rv.find(function(r){
return r && r.id === x[id];
});
if (el) {
el.children.push(x);
} else {
rv.push({ id: x[id], text: x[text], children: [x] });
}
return rv;
}, []);
}
var result = groupBy(response, "moduleID", "moduleTitle");
result.forEach(function(el){
el.children = groupBy(el.children, "categoryID", "categoryTitle");
});
The above code is working as expected, but as you can see, after the first grouping I had to iterate again over the array which was grouped by the moduleId in order to group by the categoryId.
How can I modify this code so I can only call groupBy function once on the array ?
Edit:
Sorry this might be late, but I want this done by using ES5, no Shim and no Polyfill too.
Here's one possible (although may be a bit advanced) approach:
class DefaultMap extends Map {
constructor(factory, iter) {
super(iter || []);
this.factory = factory;
}
get(key) {
if (!this.has(key))
this.set(key, this.factory());
return super.get(key);
}
}
Basically, it's the a Map that invokes a factory function when a value is missing. Now, the funny part:
let grouper = new DefaultMap(() => new DefaultMap(Array));
for (let item of yourArray) {
let firstKey = item.whatever;
let secondKey = item.somethingElse;
grouper.get(firstKey).get(secondKey).push(item);
}
For each firstKey this creates a Map inside grouper, and the values of those maps are arrays grouped by the second key.
A more interesting part of your question is that you're using compound keys, which is quite tricky in JS, since it provides (almost) no immutable data structures. Consider:
items = [
{a: 'one', b: 1},
{a: 'one', b: 1},
{a: 'one', b: 2},
{a: 'two', b: 2},
]
let grouper = new DefaultMap(Array);
for (let x of items) {
let key = [x.a, x.b]; // wrong!
grouper.get(key).push(x);
}
So, we're naively grouping objects by a compound key and expecting to see two objects under ['one', 1] in our grouper (which is one level for the sake of the example). Of course, that won't work, because each key is a freshly created array and all of them are different for Map or any other keyed storage.
One possible solution is to create an immutable structure for each key. An obvious choice would be to use Symbol, e.g.
let tuple = (...args) => Symbol.for(JSON.stringify(args))
and then
for (let x of items) {
let key = tuple(x.a, x.b); // works
grouper.get(key).push(x);
}
You could extend your function by using an array for the grouping id/names.
function groupBy(data, groups) {
return data.reduce(function (rv, x) {
groups.reduce(function (level, key) {
var el;
level.some(function (r) {
if (r && r.id === x[key[0]]) {
el = r;
return true;
}
});
if (!el) {
el = { id: x[key[0]], text: x[key[1]], children: [] };
level.push(el);
}
return el.children;
}, rv).push({ id: x.id, text: x.lib });
return rv;
}, []);
}
var response = [{ id: 1, lib: "A", categoryID: 10, categoryTitle: "Cat10", moduleID: "2", moduleTitle: "Workflow" }, { id: 2, lib: "B", categoryID: 10, categoryTitle: "Cat10", moduleID: "2", moduleTitle: "Module 2" }, { id: 110, lib: "XXX", categoryID: 90, categoryTitle: "Cat90", moduleID: "4", moduleTitle: "Module 4" }],
result = groupBy(response, [["moduleID", "moduleTitle"], ["categoryID", "categoryTitle"]]);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Version with path as id.
function groupBy(data, groups) {
return data.reduce(function (rv, x) {
var path = [];
var last = groups.reduce(function (level, key, i) {
path.length = i;
path[i] = key[0].slice(0, -2).toUpperCase() + ':' + x[key[0]];
var id = path.join(';'),
el = level.find(function (r) {
return r && r.id === id;
});
if (!el) {
el = { id: path.join(';'), text: x[key[1]], children: [] };
level.push(el);
}
return el.children;
}, rv);
last.push({ id: path.concat('NODE:' + x.id).join(';') });
return rv;
}, []);
}
var response = [{ id: 1, lib: "A", categoryID: 10, categoryTitle: "Cat10", moduleID: "2", moduleTitle: "Workflow" }, { id: 2, lib: "B", categoryID: 10, categoryTitle: "Cat10", moduleID: "2", moduleTitle: "Module 2" }, { id: 110, lib: "XXX", categoryID: 90, categoryTitle: "Cat90", moduleID: "4", moduleTitle: "Module 4" }];
var result = groupBy(response, [["moduleID", "moduleTitle"], ["categoryID", "categoryTitle"]]);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
You could do it like this:
const exit = Symbol("exit");
function groupBy(arr, ...props){
const root = {};
for(const el of arr){
const obj = props.map(key => el[key])
.reduce((obj, key) => obj[key] || (obj[key] = {}), root);
(obj[exit] || (obj[exit] = [])).push(el);
}
}
So you can access it like:
const grouped = groupBy(response, "moduleID", "moduleTitle");
console.log( grouped[2]["workflow"][exit] );
You might leave away that exit symbol, but it feels a bit wrong to mix a nested tree with arrays.

Javascript - removing elements on array [duplicate]

I have two result sets like this:
// Result 1
[
{ value: "0", display: "Jamsheer" },
{ value: "1", display: "Muhammed" },
{ value: "2", display: "Ravi" },
{ value: "3", display: "Ajmal" },
{ value: "4", display: "Ryan" }
]
// Result 2
[
{ value: "0", display: "Jamsheer" },
{ value: "1", display: "Muhammed" },
{ value: "2", display: "Ravi" },
{ value: "3", display: "Ajmal" },
]
The final result I need is the difference between these arrays ā€“ the final result should be like this:
[{ value: "4", display: "Ryan" }]
Is it possible to do something like this in JavaScript?
Using only native JS, something like this will work:
const a = [{ value:"0", display:"Jamsheer" }, { value:"1", display:"Muhammed" }, { value:"2", display:"Ravi" }, { value:"3", display:"Ajmal" }, { value:"4", display:"Ryan" }];
const b = [{ value:"0", display:"Jamsheer", $$hashKey:"008" }, { value:"1", display:"Muhammed", $$hashKey:"009" }, { value:"2", display:"Ravi", $$hashKey:"00A" }, { value:"3", display:"Ajmal", $$hashKey:"00B" }];
// A comparer used to determine if two entries are equal.
const isSameUser = (a, b) => a.value === b.value && a.display === b.display;
// Get items that only occur in the left array,
// using the compareFunction to determine equality.
const onlyInLeft = (left, right, compareFunction) =>
left.filter(leftValue =>
!right.some(rightValue =>
compareFunction(leftValue, rightValue)));
const onlyInA = onlyInLeft(a, b, isSameUser);
const onlyInB = onlyInLeft(b, a, isSameUser);
const result = [...onlyInA, ...onlyInB];
console.log(result);
For those who like one-liner solutions in ES6, something like this:
const arrayOne = [
{ value: "4a55eff3-1e0d-4a81-9105-3ddd7521d642", display: "Jamsheer" },
{ value: "644838b3-604d-4899-8b78-09e4799f586f", display: "Muhammed" },
{ value: "b6ee537a-375c-45bd-b9d4-4dd84a75041d", display: "Ravi" },
{ value: "e97339e1-939d-47ab-974c-1b68c9cfb536", display: "Ajmal" },
{ value: "a63a6f77-c637-454e-abf2-dfb9b543af6c", display: "Ryan" },
];
const arrayTwo = [
{ value: "4a55eff3-1e0d-4a81-9105-3ddd7521d642", display: "Jamsheer"},
{ value: "644838b3-604d-4899-8b78-09e4799f586f", display: "Muhammed"},
{ value: "b6ee537a-375c-45bd-b9d4-4dd84a75041d", display: "Ravi"},
{ value: "e97339e1-939d-47ab-974c-1b68c9cfb536", display: "Ajmal"},
];
const results = arrayOne.filter(({ value: id1 }) => !arrayTwo.some(({ value: id2 }) => id2 === id1));
console.log(results);
You could use Array.prototype.filter() in combination with Array.prototype.some().
Here is an example (assuming your arrays are stored in the variables result1 and result2):
//Find values that are in result1 but not in result2
var uniqueResultOne = result1.filter(function(obj) {
return !result2.some(function(obj2) {
return obj.value == obj2.value;
});
});
//Find values that are in result2 but not in result1
var uniqueResultTwo = result2.filter(function(obj) {
return !result1.some(function(obj2) {
return obj.value == obj2.value;
});
});
//Combine the two arrays of unique entries
var result = uniqueResultOne.concat(uniqueResultTwo);
import differenceBy from 'lodash/differenceBy'
const myDifferences = differenceBy(Result1, Result2, 'value')
This will return the difference between two arrays of objects, using the key value to compare them. Note two things with the same value will not be returned, as the other keys are ignored.
This is a part of lodash.
I take a slightly more general-purpose approach, although similar in ideas to the approaches of both #Cerbrus and #Kasper Moerch. I create a function that accepts a predicate to determine if two objects are equal (here we ignore the $$hashKey property, but it could be anything) and return a function which calculates the symmetric difference of two lists based on that predicate:
a = [{ value:"4a55eff3-1e0d-4a81-9105-3ddd7521d642", display:"Jamsheer"}, { value:"644838b3-604d-4899-8b78-09e4799f586f", display:"Muhammed"}, { value:"b6ee537a-375c-45bd-b9d4-4dd84a75041d", display:"Ravi"}, { value:"e97339e1-939d-47ab-974c-1b68c9cfb536", display:"Ajmal"}, { value:"a63a6f77-c637-454e-abf2-dfb9b543af6c", display:"Ryan"}]
b = [{ value:"4a55eff3-1e0d-4a81-9105-3ddd7521d642", display:"Jamsheer", $$hashKey:"008"}, { value:"644838b3-604d-4899-8b78-09e4799f586f", display:"Muhammed", $$hashKey:"009"}, { value:"b6ee537a-375c-45bd-b9d4-4dd84a75041d", display:"Ravi", $$hashKey:"00A"}, { value:"e97339e1-939d-47ab-974c-1b68c9cfb536", display:"Ajmal", $$hashKey:"00B"}]
var makeSymmDiffFunc = (function() {
var contains = function(pred, a, list) {
var idx = -1, len = list.length;
while (++idx < len) {if (pred(a, list[idx])) {return true;}}
return false;
};
var complement = function(pred, a, b) {
return a.filter(function(elem) {return !contains(pred, elem, b);});
};
return function(pred) {
return function(a, b) {
return complement(pred, a, b).concat(complement(pred, b, a));
};
};
}());
var myDiff = makeSymmDiffFunc(function(x, y) {
return x.value === y.value && x.display === y.display;
});
var result = myDiff(a, b); //=> {value="a63a6f77-c637-454e-abf2-dfb9b543af6c", display="Ryan"}
It has one minor advantage over Cerebrus's approach (as does Kasper Moerch's approach) in that it escapes early; if it finds a match, it doesn't bother checking the rest of the list. If I had a curry function handy, I would do this a little differently, but this works fine.
Explanation
A comment asked for a more detailed explanation for beginners. Here's an attempt.
We pass the following function to makeSymmDiffFunc:
function(x, y) {
return x.value === y.value && x.display === y.display;
}
This function is how we decide that two objects are equal. Like all functions that return true or false, it can be called a "predicate function", but that's just terminology. The main point is that makeSymmDiffFunc is configured with a function that accepts two objects and returns true if we consider them equal, false if we don't.
Using that, makeSymmDiffFunc (read "make symmetric difference function") returns us a new function:
return function(a, b) {
return complement(pred, a, b).concat(complement(pred, b, a));
};
This is the function we will actually use. We pass it two lists and it finds the elements in the first not in the second, then those in the second not in the first and combine these two lists.
Looking over it again, though, I could definitely have taken a cue from your code and simplified the main function quite a bit by using some:
var makeSymmDiffFunc = (function() {
var complement = function(pred, a, b) {
return a.filter(function(x) {
return !b.some(function(y) {return pred(x, y);});
});
};
return function(pred) {
return function(a, b) {
return complement(pred, a, b).concat(complement(pred, b, a));
};
};
}());
complement uses the predicate and returns the elements of its first list not in its second. This is simpler than my first pass with a separate contains function.
Finally, the main function is wrapped in an immediately invoked function expression (IIFE) to keep the internal complement function out of the global scope.
Update, a few years later
Now that ES2015 has become pretty well ubiquitous, I would suggest the same technique, with a lot less boilerplate:
const diffBy = (pred) => (a, b) => a.filter(x => !b.some(y => pred(x, y)))
const makeSymmDiffFunc = (pred) => (a, b) => diffBy(pred)(a, b).concat(diffBy(pred)(b, a))
const myDiff = makeSymmDiffFunc((x, y) => x.value === y.value && x.display === y.display)
const result = myDiff(a, b)
//=> {value="a63a6f77-c637-454e-abf2-dfb9b543af6c", display="Ryan"}
In addition, say two object array with different key value
// Array Object 1
const arrayObjOne = [
{ userId: "1", display: "Jamsheer" },
{ userId: "2", display: "Muhammed" },
{ userId: "3", display: "Ravi" },
{ userId: "4", display: "Ajmal" },
{ userId: "5", display: "Ryan" }
]
// Array Object 2
const arrayObjTwo =[
{ empId: "1", display: "Jamsheer", designation:"Jr. Officer" },
{ empId: "2", display: "Muhammed", designation:"Jr. Officer" },
{ empId: "3", display: "Ravi", designation:"Sr. Officer" },
{ empId: "4", display: "Ajmal", designation:"Ast. Manager" },
]
You can use filter in es5 or native js to substract two array object.
//Find data that are in arrayObjOne but not in arrayObjTwo
var uniqueResultArrayObjOne = arrayObjOne.filter(function(objOne) {
return !arrayObjTwo.some(function(objTwo) {
return objOne.userId == objTwo.empId;
});
});
In ES6 you can use Arrow function with Object destructuring of ES6.
const ResultArrayObjOne = arrayObjOne.filter(({ userId: userId }) => !arrayObjTwo.some(({ empId: empId }) => empId === userId));
console.log(ResultArrayObjOne);
You can create an object with keys as the unique value corresponding for each object in array and then filter each array based on existence of the key in other's object. It reduces the complexity of the operation.
ES6
let a = [{ value:"4a55eff3-1e0d-4a81-9105-3ddd7521d642", display:"Jamsheer"}, { value:"644838b3-604d-4899-8b78-09e4799f586f", display:"Muhammed"}, { value:"b6ee537a-375c-45bd-b9d4-4dd84a75041d", display:"Ravi"}, { value:"e97339e1-939d-47ab-974c-1b68c9cfb536", display:"Ajmal"}, { value:"a63a6f77-c637-454e-abf2-dfb9b543af6c", display:"Ryan"}];
let b = [{ value:"4a55eff3-1e0d-4a81-9105-3ddd7521d642", display:"Jamsheer", $$hashKey:"008"}, { value:"644838b3-604d-4899-8b78-09e4799f586f", display:"Muhammed", $$hashKey:"009"}, { value:"b6ee537a-375c-45bd-b9d4-4dd84a75041d", display:"Ravi", $$hashKey:"00A"}, { value:"e97339e1-939d-47ab-974c-1b68c9cfb536", display:"Ajmal", $$hashKey:"00B"}];
let valuesA = a.reduce((a,{value}) => Object.assign(a, {[value]:value}), {});
let valuesB = b.reduce((a,{value}) => Object.assign(a, {[value]:value}), {});
let result = [...a.filter(({value}) => !valuesB[value]), ...b.filter(({value}) => !valuesA[value])];
console.log(result);
ES5
var a = [{ value:"4a55eff3-1e0d-4a81-9105-3ddd7521d642", display:"Jamsheer"}, { value:"644838b3-604d-4899-8b78-09e4799f586f", display:"Muhammed"}, { value:"b6ee537a-375c-45bd-b9d4-4dd84a75041d", display:"Ravi"}, { value:"e97339e1-939d-47ab-974c-1b68c9cfb536", display:"Ajmal"}, { value:"a63a6f77-c637-454e-abf2-dfb9b543af6c", display:"Ryan"}];
var b = [{ value:"4a55eff3-1e0d-4a81-9105-3ddd7521d642", display:"Jamsheer", $$hashKey:"008"}, { value:"644838b3-604d-4899-8b78-09e4799f586f", display:"Muhammed", $$hashKey:"009"}, { value:"b6ee537a-375c-45bd-b9d4-4dd84a75041d", display:"Ravi", $$hashKey:"00A"}, { value:"e97339e1-939d-47ab-974c-1b68c9cfb536", display:"Ajmal", $$hashKey:"00B"}];
var valuesA = a.reduce(function(a,c){a[c.value] = c.value; return a; }, {});
var valuesB = b.reduce(function(a,c){a[c.value] = c.value; return a; }, {});
var result = a.filter(function(c){ return !valuesB[c.value]}).concat(b.filter(function(c){ return !valuesA[c.value]}));
console.log(result);
I found this solution using filter and some.
resultFilter = (firstArray, secondArray) => {
return firstArray.filter(firstArrayItem =>
!secondArray.some(
secondArrayItem => firstArrayItem._user === secondArrayItem._user
)
);
};
I think the #Cerbrus solution is spot on. I have implemented the same solution but extracted the repeated code into it's own function (DRY).
function filterByDifference(array1, array2, compareField) {
var onlyInA = differenceInFirstArray(array1, array2, compareField);
var onlyInb = differenceInFirstArray(array2, array1, compareField);
return onlyInA.concat(onlyInb);
}
function differenceInFirstArray(array1, array2, compareField) {
return array1.filter(function (current) {
return array2.filter(function (current_b) {
return current_b[compareField] === current[compareField];
}).length == 0;
});
}
you can do diff a on b and diff b on a, then merge both results
let a = [
{ value: "0", display: "Jamsheer" },
{ value: "1", display: "Muhammed" },
{ value: "2", display: "Ravi" },
{ value: "3", display: "Ajmal" },
{ value: "4", display: "Ryan" }
]
let b = [
{ value: "0", display: "Jamsheer" },
{ value: "1", display: "Muhammed" },
{ value: "2", display: "Ravi" },
{ value: "3", display: "Ajmal" }
]
// b diff a
let resultA = b.filter(elm => !a.map(elm => JSON.stringify(elm)).includes(JSON.stringify(elm)));
// a diff b
let resultB = a.filter(elm => !b.map(elm => JSON.stringify(elm)).includes(JSON.stringify(elm)));
// show merge
console.log([...resultA, ...resultB]);
let obj1 =[
{ id: 1, submenu_name: 'login' },
{ id: 2, submenu_name: 'Profile',},
{ id: 3, submenu_name: 'password', },
{ id: 4, submenu_name: 'reset',}
] ;
let obj2 =[
{ id: 2},
{ id: 3 },
] ;
// Need Similar obj
const result1 = obj1.filter(function(o1){
return obj2.some(function(o2){
return o1.id == o2.id; // id is unnique both array object
});
});
console.log(result1);
// Need differnt obj
const result2 = obj1.filter(function(o1){
return !obj2.some(function(o2){ // for diffrent we use NOT (!) befor obj2 here
return o1.id == o2.id; // id is unnique both array object
});
});
console.log(result2);
JavaScript has Maps, that provide O(1) insertion and lookup time. Therefore this can be solved in O(n) (and not O(nĀ²) as all the other answers do). For that, it is necessary to generate a unique primitive (string / number) key for each object. One could JSON.stringify, but that's quite error prone as the order of elements could influence equality:
JSON.stringify({ a: 1, b: 2 }) !== JSON.stringify({ b: 2, a: 1 })
Therefore, I'd take a delimiter that does not appear in any of the values and compose a string manually:
const toHash = value => value.value + "#" + value.display;
Then a Map gets created. When an element exists already in the Map, it gets removed, otherwise it gets added. Therefore only the elements that are included odd times (meaning only once) remain. This will only work if the elements are unique in each array:
const entries = new Map();
for(const el of [...firstArray, ...secondArray]) {
const key = toHash(el);
if(entries.has(key)) {
entries.delete(key);
} else {
entries.set(key, el);
}
}
const result = [...entries.values()];
const firstArray = [
{ value: "0", display: "Jamsheer" },
{ value: "1", display: "Muhammed" },
{ value: "2", display: "Ravi" },
{ value: "3", display: "Ajmal" },
{ value: "4", display: "Ryan" }
]
const secondArray = [
{ value: "0", display: "Jamsheer" },
{ value: "1", display: "Muhammed" },
{ value: "2", display: "Ravi" },
{ value: "3", display: "Ajmal" },
];
const toHash = value => value.value + "#" + value.display;
const entries = new Map();
for(const el of [...firstArray, ...secondArray]) {
const key = toHash(el);
if(entries.has(key)) {
entries.delete(key);
} else {
entries.set(key, el);
}
}
const result = [...entries.values()];
console.log(result);
Most of the observed code does not examine the whole object and only compares the value of a particular method. This solution is the same, except that you can specify that method yourself.
Here is an example:
const arr1 = [
{
id: 1,
name: "Tom",
scores: {
math: 80,
science: 100
}
},
{
id: 2,
name: "John",
scores: {
math: 50,
science: 70
}
}
];
const arr2 = [
{
id: 1,
name: "Tom",
scores: {
math: 80,
science: 70
}
}
];
function getDifference(array1, array2, attr) {
return array1.filter(object1 => {
return !array2.some(object2 => {
return eval("object1." + attr + " == object2." + attr);
});
});
}
// šŸ‘‡ļø [{id: 2, name: 'John'...
console.log(getDifference(arr1, arr2, "id"));
// šŸ‘‡ļø [{id: 2, name: 'John'...
console.log(getDifference(arr1, arr2, "scores.math"));
// šŸ‘‡ļø [{id: 1, name: 'Tom'...
console.log(getDifference(arr1, arr2, "scores.science"));
Most of answers here are rather complex, but isn't logic behind this quite simple?
check which array is longer and provide it as first parameter (if length is equal, parameters order doesnt matter)
Iterate over array1.
For current iteration element of array1 check if it is present in array2
If it is NOT present, than
Push it to 'difference' array
const getArraysDifference = (longerArray, array2) => {
const difference = [];
longerArray.forEach(el1 => { /*1*/
el1IsPresentInArr2 = array2.some(el2 => el2.value === el1.value); /*2*/
if (!el1IsPresentInArr2) { /*3*/
difference.push(el1); /*4*/
}
});
return difference;
}
O(n^2) complexity.
I prefer map object when it comes to big arrays.
// create tow arrays
array1 = Array.from({length: 400},() => ({value:Math.floor(Math.random() * 4000)}))
array2 = Array.from({length: 400},() => ({value:Math.floor(Math.random() * 4000)}))
// calc diff with some function
console.time('diff with some');
results = array2.filter(({ value: id1 }) => array1.some(({ value: id2 }) => id2 === id1));
console.log('diff results ',results.length)
console.timeEnd('diff with some');
// calc diff with map object
console.time('diff with map');
array1Map = {};
for(const item1 of array1){
array1Map[item1.value] = true;
}
results = array2.filter(({ value: id2 }) => array1Map[id2]);
console.log('map results ',results.length)
console.timeEnd('diff with map');
Having:
const array = [{id:3, name:'xx'} , {id:7, name:'yy'}, {id:9, name:'zz'}];
const array2 =[3,4,5];//These are a group of ids
I made a function that removes the objects from array that matches de ids within array2, like this:
export const aFilter = (array, array2) => {
array2.forEach(element => {
array = array.filter(item=> item.id !== element);
});
return array;
}
After calling the function we should have the array with no object wit id = 3
const rta = aFilter(array, array2);
//rta should be = [{id:7, name:'yy'}, {id:9, name:'zz'}];
It worked for me, and was pretty easy
I've made a generalized diff that compare 2 objects of any kind and can run a modification handler
gist.github.com/bortunac "diff.js"
an ex of using :
old_obj={a:1,b:2,c:[1,2]}
now_obj={a:2 , c:[1,3,5],d:55}
so property a is modified, b is deleted, c modified, d is added
var handler=function(type,pointer){
console.log(type,pointer,this.old.point(pointer)," | ",this.now.point(pointer));
}
now use like
df=new diff();
df.analize(now_obj,old_obj);
df.react(handler);
the console will show
mdf ["a"] 1 | 2
mdf ["c", "1"] 2 | 3
add ["c", "2"] undefined | 5
add ["d"] undefined | 55
del ["b"] 2 | undefined
Most generic and simple way:
findObject(listOfObjects, objectToSearch) {
let found = false, matchingKeys = 0;
for(let object of listOfObjects) {
found = false;
matchingKeys = 0;
for(let key of Object.keys(object)) {
if(object[key]==objectToSearch[key]) matchingKeys++;
}
if(matchingKeys==Object.keys(object).length) {
found = true;
break;
}
}
return found;
}
get_removed_list_of_objects(old_array, new_array) {
// console.log('old:',old_array);
// console.log('new:',new_array);
let foundList = [];
for(let object of old_array) {
if(!this.findObject(new_array, object)) foundList.push(object);
}
return foundList;
}
get_added_list_of_objects(old_array, new_array) {
let foundList = [];
for(let object of new_array) {
if(!this.findObject(old_array, object)) foundList.push(object);
}
return foundList;
}
I came across this question while searching for a way to pick out the first item in one array that does not match any of the values in another array and managed to sort it out eventually with array.find() and array.filter() like this
var carList= ['mercedes', 'lamborghini', 'bmw', 'honda', 'chrysler'];
var declinedOptions = ['mercedes', 'lamborghini'];
const nextOption = carList.find(car=>{
const duplicate = declinedOptions.filter(declined=> {
return declined === car
})
console.log('duplicate:',duplicate) //should list out each declined option
if(duplicate.length === 0){//if theres no duplicate, thats the nextOption
return car
}
})
console.log('nextOption:', nextOption);
//expected outputs
//duplicate: mercedes
//duplicate: lamborghini
//duplicate: []
//nextOption: bmw
if you need to keep fetching an updated list before cross-checking for the next best option this should work well enough :)
#atheane response : fork :
const newList = [
{ value: "4a55eff3-1e0d-4a81-9105-3ddd7521d642", display: "Jamsheer" },
{ value: "644838b3-604d-4899-8b78-09e4799f586f", display: "Muhammed" },
{ value: "b6ee537a-375c-45bd-b9d4-4dd84a75041d", display: "Ravi2" },
{ value: "a63a6f77-c637-454e-abf2-dfb9b543af6c", display: "Ryan" },
];
const oldList = [
{ value: "4a55eff3-1e0d-4a81-9105-3ddd7521d642", display: "Jamsheer"},
{ value: "644838b3-604d-4899-8b78-09e4799f586f", display: "Muhammed"},
{ value: "b6ee537a-375c-45bd-b9d4-4dd84a75041d", display: "Ravi"},
{ value: "e97339e1-939d-47ab-974c-1b68c9cfb536", display: "Ajmal"},
];
const resultsAdd = newList.filter(({ value: id1 }) => !oldList.some(({ value: id2 }) => id2 === id1));
const resultsRemove = oldList.filter(({ value: id1 }) => !newList.some(({ value: id2 }) => id2 === id1));
const resultsUpdate = newList.filter(({ value: id1, ...rest1 }) => oldList.some(({ value: id2, ...rest2 }) => id2 === id1 && JSON.stringify(rest1) !== JSON.stringify(rest2)));
console.log("added", resultsAdd);
console.log("removed", resultsRemove);
console.log("updated", resultsUpdate);
Most simple way could be use of filter and some together
Please refer below link
DifferenceInTwoArrayOfObjectInSimpleWay
If you are willing to use external libraries, You can use _.difference in underscore.js to achieve this. _.difference returns the values from array that are not present in the other arrays.
_.difference([1,2,3,4,5][1,4,10])
==>[2,3,5]

How can I check if the array of objects have duplicate property values?

I need some help with iterating through array, I keep getting stuck or reinventing the wheel.
values = [
{ name: 'someName1' },
{ name: 'someName2' },
{ name: 'someName1' },
{ name: 'someName1' }
]
How could I check if there are two (or more) same name value in array? I do not need a counter, just setting some variable if array values are not unique. Have in mind that array length is dynamic, also array values.
Use array.prototype.map and array.prototype.some:
var values = [
{ name: 'someName1' },
{ name: 'someName2' },
{ name: 'someName4' },
{ name: 'someName2' }
];
var valueArr = values.map(function(item){ return item.name });
var isDuplicate = valueArr.some(function(item, idx){
return valueArr.indexOf(item) != idx
});
console.log(isDuplicate);
ECMA Script 6 Version
If you are in an environment which supports ECMA Script 6's Set, then you can use Array.prototype.some and a Set object, like this
let seen = new Set();
var hasDuplicates = values.some(function(currentObject) {
return seen.size === seen.add(currentObject.name).size;
});
Here, we insert each and every object's name into the Set and we check if the size before and after adding are the same. This works because Set.size returns a number based on unique data (set only adds entries if the data is unique). If/when you have duplicate names, the size won't increase (because the data won't be unique) which means that we would have already seen the current name and it will return true.
ECMA Script 5 Version
If you don't have Set support, then you can use a normal JavaScript object itself, like this
var seen = {};
var hasDuplicates = values.some(function(currentObject) {
if (seen.hasOwnProperty(currentObject.name)) {
// Current name is already seen
return true;
}
// Current name is being seen for the first time
return (seen[currentObject.name] = false);
});
The same can be written succinctly, like this
var seen = {};
var hasDuplicates = values.some(function (currentObject) {
return seen.hasOwnProperty(currentObject.name)
|| (seen[currentObject.name] = false);
});
Note: In both the cases, we use Array.prototype.some because it will short-circuit. The moment it gets a truthy value from the function, it will return true immediately, it will not process rest of the elements.
In TS and ES6 you can create a new Set with the property to be unique and compare it's size to the original array.
const values = [
{ name: 'someName1' },
{ name: 'someName2' },
{ name: 'someName3' },
{ name: 'someName1' }
]
const uniqueValues = new Set(values.map(v => v.name));
if (uniqueValues.size < values.length) {
console.log('duplicates found')
}
To know if simple array has duplicates we can compare first and last indexes of the same value:
The function:
var hasDupsSimple = function(array) {
return array.some(function(value) { // .some will break as soon as duplicate found (no need to itterate over all array)
return array.indexOf(value) !== array.lastIndexOf(value); // comparing first and last indexes of the same value
})
}
Tests:
hasDupsSimple([1,2,3,4,2,7])
// => true
hasDupsSimple([1,2,3,4,8,7])
// => false
hasDupsSimple([1,"hello",3,"bye","hello",7])
// => true
For an array of objects we need to convert the objects values to a simple array first:
Converting array of objects to the simple array with map:
var hasDupsObjects = function(array) {
return array.map(function(value) {
return value.suit + value.rank
}).some(function(value, index, array) {
return array.indexOf(value) !== array.lastIndexOf(value);
})
}
Tests:
var cardHand = [
{ "suit":"spades", "rank":"ten" },
{ "suit":"diamonds", "rank":"ace" },
{ "suit":"hearts", "rank":"ten" },
{ "suit":"clubs", "rank":"two" },
{ "suit":"spades", "rank":"three" },
]
hasDupsObjects(cardHand);
// => false
var cardHand2 = [
{ "suit":"spades", "rank":"ten" },
{ "suit":"diamonds", "rank":"ace" },
{ "suit":"hearts", "rank":"ten" },
{ "suit":"clubs", "rank":"two" },
{ "suit":"spades", "rank":"ten" },
]
hasDupsObjects(cardHand2);
// => true
if you are looking for a boolean, the quickest way would be
var values = [
{ name: 'someName1' },
{ name: 'someName2' },
{ name: 'someName1' },
{ name: 'someName1' }
]
// solution
var hasDuplicate = false;
values.map(v => v.name).sort().sort((a, b) => {
if (a === b) hasDuplicate = true
})
console.log('hasDuplicate', hasDuplicate)
const values = [
{ name: 'someName1' },
{ name: 'someName2' },
{ name: 'someName4' },
{ name: 'someName4' }
];
const foundDuplicateName = values.find((nnn, index) =>{
return values.find((x, ind)=> x.name === nnn.name && index !== ind )
})
console.log(foundDuplicateName)
Found the first one duplicate name
const values = [
{ name: 'someName1' },
{ name: 'someName2' },
{ name: 'someName4' },
{ name: 'someName4' }
];
const foundDuplicateName = values.find((nnn, index) =>{
return values.find((x, ind)=> x.name === nnn.name && index !== ind )
})
You just need one line of code.
var values = [
{ name: 'someName1' },
{ name: 'someName2' },
{ name: 'someName4' },
{ name: 'someName2' }
];
let hasDuplicates = values.map(v => v.name).length > new Set(values.map(v => v.name)).size ? true : false;
Try an simple loop:
var repeat = [], tmp, i = 0;
while(i < values.length){
repeat.indexOf(tmp = values[i++].name) > -1 ? values.pop(i--) : repeat.push(tmp)
}
Demo
With Underscore.js A few ways with Underscore can be done. Here is one of them. Checking if the array is already unique.
function isNameUnique(values){
return _.uniq(values, function(v){ return v.name }).length == values.length
}
With vanilla JavaScript
By checking if there is no recurring names in the array.
function isNameUnique(values){
var names = values.map(function(v){ return v.name });
return !names.some(function(v){
return names.filter(function(w){ return w==v }).length>1
});
}
//checking duplicate elements in an array
var arr=[1,3,4,6,8,9,1,3,4,7];
var hp=new Map();
console.log(arr.sort());
var freq=0;
for(var i=1;i<arr.length;i++){
// console.log(arr[i-1]+" "+arr[i]);
if(arr[i]==arr[i-1]){
freq++;
}
else{
hp.set(arr[i-1],freq+1);
freq=0;
}
}
console.log(hp);
You can use map to return just the name, and then use this forEach trick to check if it exists at least twice:
var areAnyDuplicates = false;
values.map(function(obj) {
return obj.name;
}).forEach(function (element, index, arr) {
if (arr.indexOf(element) !== index) {
areAnyDuplicates = true;
}
});
Fiddle
Adding updated es6 function to check for unique and duplicate values in array. This function is modular and can be reused throughout the code base. Thanks to all the post above.
/* checks for unique keynames in array */
const checkForUnique = (arrToCheck, keyName) => {
/* make set to remove duplicates and compare to */
const uniqueValues = [...new Set(arrToCheck.map(v => v[keyName]))];
if(arrToCheck.length !== uniqueValues.length){
console.log('NOT UNIQUE')
return false
}
return true
}
let arr = [{name:'joshua'},{name:'tony'},{name:'joshua'}]
/* call function with arr and key to check for */
let isUnique = checkForUnique(arr,'name')
checkDuplicate(arr, item) {
const uniqueValues = new Set(arr.map((v) => v[item]));
return uniqueValues.size < arr.length;
},
console.log(this.checkDuplicate(this.dutyExemptionBase, 'CI_ExemptionType')); // true || false
It is quite interesting to work with arrays
You can use new Set() method to find duplicate values!
let's assume you have an array of objects like this...
let myArray = [
{ id: 0, name: "Jhon" },
{ id: 1, name: "sara" },
{ id: 2, name: "pop" },
{ id: 3, name: "sara" }
]
const findUnique = new Set(myArray.map(x => {
return x.name
}))
if(findUnique.size < myArray.length){
console.log("duplicates found!")
}else{
console.log("Done!")
}
const duplicateValues = [{ name: "abc" }, { name: "bcv" }, { name: "abc" }];
const isContainDuplicate = (params) => {
const removedDuplicate = new Set(params.map((el) => el.name));
return params.length !== removedDuplicate.size;
};
const isDuplicate = isContainDuplicate(duplicateValues);
console.log("isDuplicate");

Categories