Compare two javascript arrays and remove duplicate entries - javascript

I am trying to compare two different arrays together, One previous and one current. The previous set of data contains:
[
{"member_name":"Test1","item":"Sword"},
{"member_name":"Test2","item":"Sword"}
]
The new set contains:
[
{"member_name":"Test1","item":"Sword"},
{"member_name":"Test2","item":"Sword"},
{"member_name":"Test1","item":"Shield"}
]
So as you can see here Test1 has obtained a new item. I've tried to compare these two arrays together a few ways but with no avail.
The methods I've tried:
This one just returns the entire array. Not individual items.
Items_Curr.filter(function(item) { return !Items_Prev.includes(item); });
This method just returns 3 Undefined's.
Items_Curr.map(e => { e.member_name });
I've been looking through trying to find a way of doing this but other posts just explain methods to determine change in simpler arrays.
E.G [a,b] - [a, b, c]
Update:
The end goal is I'd like to create a new array 'NewItems' which would contain an array of all the newly added names and items. So if there is change, I would like that to be broadcasted, if there is no change, then ignore until the function has been ran again.

Realistically you want to do something like:
[a, b, c] - [a, b]
Which would give you c. You can achieve this using .some, which allows you to "customize" the includes functionality.
See example below:
const arr1 = [
{"member_name":"Test1","item":"Sword"},
{"member_name":"Test2","item":"Sword"}
];
const arr2 = [
{"member_name":"Test1","item":"Sword"},
{"member_name":"Test2","item":"Sword"},
{"member_name":"Test1","item":"Shield"}
];
const res = arr2.filter(({member_name:a, item:x}) => !arr1.some(({member_name:b, item:y}) => a === b && x === y));
console.log(res);

If you know your properties will always be in the same order you can use JSON.stringify to serialize the objects and compare the results:
const Items_Prev = [
{"member_name":"Test1","item":"Sword"},
{"member_name":"Test2","item":"Sword"}
]
const Items_Curr = [
{"member_name":"Test1","item":"Sword"},
{"member_name":"Test2","item":"Sword"},
{"member_name":"Test1","item":"Shield"}
]
const serialized_Items_Prev = Items_Prev.map(i => JSON.stringify(i));
const NewItems = Items_Curr.filter(i => !serialized_Items_Prev.includes(JSON.stringify(i)));
console.log(NewItems);

I think something like this you need to do if keys of objects are not changing and new items are only added at last.
const array1 = [
{"member_name":"Test1","item":"Sword"},
{"member_name":"Test2","item":"Sword"}
];
const array2 = [
{"member_name":"Test1","item":"Sword"},
{"member_name":"Test2","item":"Sword"},
{"member_name":"Test1","item":"Shield"}
];
const compare = (array1, array2) => {
if (array1.length !== array2.length) {
return false;
}
for (let i = 0; i < array1.length; i += 1) {
if (array1[i].member_name !== array2[i].member_name) {
return false;
}
if (array1[i].item !== array2[i].item) {
return false;
}
}
return true;
};
console.log(compare(array1, array2));
If order of objects are changing, then you need to write sorting algo for array and then compare.

You can acheieve it using array methods filter() and findIndex()
Filter current array with out put of findIndex() function on previous array
var prevItems = [
{"member_name":"Test1","item":"Sword"},
{"member_name":"Test2","item":"Sword"}
]
var currItems = [
{"member_name":"Test1","item":"Sword"},
{"member_name":"Test2","item":"Sword"},
{"member_name":"Test1","item":"Shield"},
{"member_name":"Test2","item":"Shield"}
]
var newItems = currItems.filter(function(currItem ){
return prevItems.findIndex(function(prevItem){
return prevItem.member_name == currItem.member_name &&
prevItem.item == currItem.item
}) == -1
})
console.log(newItems)

Related

Filter array for objects with nested array that contains a specific string

I am trying to filter a large multi dimensional array, I know I can re build the array but I was wondering if there was a way of me filtering this array based on if the key "d" contains "d3.1"
const arr = [
{a:"a1",b:"b1",c:[{d:"d1.1",e:"e1.1"},{d:"d1.2",e:"e1.2"}]},
{a:"a2",b:"b2",c:[{d:"d2.1",e:"e2.1"},{d:"d2.2",e:"e2.2"}]},
{a:"a3",b:"b3",c:[{d:"d3.1",e:"e3.1"},{d:"d3.2",e:"e3.2"}]},
{a:"a4",b:"b4",c:[{d:"d4.1",e:"e4.1"},{d:"d4.2",e:"e4.2"}]},
{a:"a1",b:"b1",c:[{d:"d1.1",e:"e1.1"},{d:"d3.1",e:"e1.2"}]},
{a:"a5",b:"b5",c:[{d:"d3.1",e:"e3.1"},{d:"d3.2",e:"e3.2"}]}
]
I have tried using .some and .filter but as it's an object within an array of objects I can't seem to get it to work.
My required output would be to have all objects where d contains "d3.1" for example:
output:
[
{a:"a3",b:"b3",c:[{d:"d3.1",e:"e3.1"},{d:"d3.2",e:"e3.2"}]},
{a:"a1",b:"b1",c:[{d:"d1.1",e:"e1.1"},{d:"d3.1",e:"e1.2"}]},
{a:"a5",b:"b5",c:[{d:"d3.1",e:"e3.1"},{d:"d3.2",e:"e3.2"}]}
]
Here is a quick solution:
const arr = [
{a:"a1",b:"b1",c:[{d:"d1.1",e:"e1.1"},{d:"d1.2",e:"e1.2"}]},
{a:"a2",b:"b2",c:[{d:"d2.1",e:"e2.1"},{d:"d2.2",e:"e2.2"}]},
{a:"a3",b:"b3",c:[{d:"d3.1",e:"e3.1"},{d:"d3.2",e:"e3.2"}]},
{a:"a4",b:"b4",c:[{d:"d4.1",e:"e4.1"},{d:"d4.2",e:"e4.2"}]},
{a:"a1",b:"b1",c:[{d:"d1.1",e:"e1.1"},{d:"d3.1",e:"e1.2"}]},
{a:"a5",b:"b5",c:[{d:"d3.1",e:"e3.1"},{d:"d3.2",e:"e3.2"}]}
]
console.log(arr.filter(el => JSON.stringify(el).includes("d3.1")))
If you want to filter the array for elements with property c containing at least one element (i.e. .some(...)) with property d being equal to "d3.1" you can use this solution:
const arr = [
{a:"a1",b:"b1",c:[{d:"d1.1",e:"e1.1"},{d:"d1.2",e:"e1.2"}]},
{a:"a2",b:"b2",c:[{d:"d2.1",e:"e2.1"},{d:"d2.2",e:"e2.2"}]},
{a:"a3",b:"b3",c:[{d:"d3.1",e:"e3.1"},{d:"d3.2",e:"e3.2"}]},
{a:"a4",b:"b4",c:[{d:"d4.1",e:"e4.1"},{d:"d4.2",e:"e4.2"}]},
{a:"a1",b:"b1",c:[{d:"d1.1",e:"e1.1"},{d:"d3.1",e:"e1.2"}]},
{a:"a5",b:"b5",c:[{d:"d3.1",e:"e3.1"},{d:"d3.2",e:"e3.2"}]}
]
const filtered = arr.filter(e => e.c.some(x => x.d == "d3.1"))
console.log(filtered)
Otherwise, please specify your requirements closer...
You could use a recursive approach to find matching array elements, creating a function like findMatch() that accepts a customizable predicate function.
This function could return true for exact matches or use regex expressions etc.
We'd then using this with Array.filter() to return the required result.
const arr = [ {a:"a1",b:"b1",c:[{d:"d1.1",e:"e1.1"},{d:"d1.2",e:"e1.2"}]}, {a:"a2",b:"b2",c:[{d:"d2.1",e:"e2.1"},{d:"d2.2",e:"e2.2"}]}, {a:"a3",b:"b3",c:[{d:"d3.1",e:"e3.1"},{d:"d3.2",e:"e3.2"}]}, {a:"a4",b:"b4",c:[{d:"d4.1",e:"e4.1"},{d:"d4.2",e:"e4.2"}]}, {a:"a1",b:"b1",c:[{d:"d1.1",e:"e1.1"},{d:"d3.1",e:"e1.2"}]}, {a:"a5",b:"b5",c:[{d:"d3.1",e:"e3.1"},{d:"d3.2",e:"e3.2"}]} ]
function findMatch(obj, predicate) {
for(let k in obj) {
if (obj[k] && typeof(obj[k]) === 'object') {
if (findMatch(obj[k], predicate)) return true;
} else if (predicate(k, obj[k])) {
return true;
}
}
}
// Custom predicate, change as needed.
const predicate = (key, value) => key === 'd' && value === 'd3.1';
const result = arr.filter(el => findMatch(el, predicate));
console.log('Result:');
result.forEach(el => console.log(JSON.stringify(el)));
.as-console-wrapper { max-height: 100% !important; }
You can use nested foreach to get your desired output, I have consoled both array(one containing the output you want and the other containing the indexes of those outputs)
values_ar = [];
index_ar = [];
arr.forEach((items, indexs)=>{
const test = arr[indexs].c;
test.forEach((item, index)=>{
if(test[index].d == "d3.1"){
values_ar.push(items)
index_ar.push(indexs)
}
})
})
console.log(values_ar)
console.log(index_ar)
Hope that answers your question.

Marge array in a function both sorted and unsorted

I have a function where I pass arrays that are both sorted and unsorted. The function working when both arrays are sorted but when I pass unsorted then it doesn't sort the array and marge it.
Example code:
// Marge sorted array
const arrayOne = [1,3,5,11,18];
const arrayTwo = [9, 15, 111, 131, 172];
function margeArray(arrOne, arrTwo){
const margedArray = [];
let arrOneItem = arrOne[0];
let arrTwoItem = arrTwo[0]
let i = 1;
let j = 1;
//Check input
if(arrOne.length === 0) {
return arrTwo;
};
if(arrTwo.length === 0) {
return arrOne;
};
while(arrOneItem || arrTwoItem) {
if(!arrTwoItem || arrOneItem < arrTwoItem) {
margedArray.push(arrOneItem);
arrOneItem = arrOne[i];
i++;
}else {
margedArray.push(arrTwoItem);
arrTwoItem = arrTwo[j];
j++;
}
}
return margedArray;
}
margeArray(arrayOne, arrayTwo)
You can use the spread syntax [...] to join the arrays and sort to sort the merge.
// Merge sorted array
const arrayOne = [11,18,1,3,5];
const arrayTwo = [9,131,172,15,111];
const msorted = [...arrayOne, ...arrayTwo].sort((a,b) => a-b);
console.log(msorted)
You implicitly assume the next added element will be smaller than any other element in the remaining part of both your arrays. This is only true when both input arrays are already sorted. So it's perfectly normal to get an incorrect result when passing unsorted arrays to your function.
Of course you can use JavaScript native array methods to do the job in one line of code, relying on primitives that are being tested a few million times per second across the browsers of the world since 1995 :)
That seems like the wise thing to do.
Why don't you want just use native sort method?:
function margeArra(arrOne, arrTwo) {
return [...arrOne, ...arrTwo].sort(function(a, b) {
return a - b;
})
}
You can do this a lot easier by .concatenating the two arrays and then .sort them numerically.
// Merge sorted array
const arrayOne = [1,3,5,11,18];
const arrayTwo = [9, 15, 111, 131, 172];
function mergeArray(arrOne, arrTwo){
return arrOne.concat(arrTwo).sort((a,b) => a-b)
}
console.log(mergeArray(arrayOne, arrayTwo));

print difference value available in array using node js

I don't know how this is possible i want to print array which contain same value and difference value.
I want to check if the value is present in both the array if present then print it in one array which contain all the same value of both array and another array which contain name which is difference.
readFileArray:
[
[
mainFolder/abc_doc.txt,
mainFolder/pqr_doc.txt,
mainFolder/subFolder/xyz_image.jpg,
mainFolder/subFolder/iop_pdf.pdf,
],
[
Fish,
Life,
Qwerty,
Moon
]
]
comparePathName:
[
mainFolder/abc_doc.txt,
mainFolder/pqr_doc.txt,
mainFolder/subFolder/xyz_image.jpg,
mainFolder/subFolder/iop_pdf.pdf,
mainFolder/fish.txt,
mainFolder/life.txt,
mainFolder/subFolder/qwerty.jpg,
mainFolder/subFolder/moon.pdf,
]
code:
for (let i = 0; i <= readFileArray.length - 1; i++) {
for (let j = 0; j < readFileArray[i].length - 1; j++) {
if (readFileArray[i][j] === comparePathName) {
availableName = readFileArray[1][j];
if (typeof availableName !== undefined) {
console.log(availableName)
}
}
}
}
output:
the value which are present in both the array i am getting it in availableName
availableName=
[
mainFolder/abc_doc.txt,
mainFolder/pqr_doc.txt,
mainFolder/subFolder/xyz_image.jpg,
mainFolder/subFolder/iop_pdf.pdf,
]
Now I also need the non-similar value in my new array which name is expectedArray
expectedArray=
[
mainFolder/fish.txt,
mainFolder/life.txt,
mainFolder/subFolder/qwerty.jpg,
mainFolder/subFolder/moon.pdf,
]
We can create functions to get the intersection and difference of the two arrays as below, then we call them by passing the relevant values.
I've decided to use Set.has rather than Array.includes to improve performance (though this would only make a difference for very large arrays)
const readFileArray = [ [ 'mainFolder/abc_doc.txt', 'mainFolder/pqr_doc.txt', 'mainFolder/subFolder/xyz_image.jpg', 'mainFolder/subFolder/iop_pdf.pdf', ], [ 'Fish', 'Life', 'Qwerty', 'Moon' ] ]
const comparePathName = [ 'mainFolder/abc_doc.txt', 'mainFolder/pqr_doc.txt', 'mainFolder/subFolder/xyz_image.jpg', 'mainFolder/subFolder/iop_pdf.pdf', 'mainFolder/fish.txt', 'mainFolder/life.txt', 'mainFolder/subFolder/qwerty.jpg', 'mainFolder/subFolder/moon.pdf', ]
/* Return all the items in one array only, use sets to avoid N squared behaviour */
function getSymmetricDifference(array1, array2) {
const set1 = new Set(array1);
const set2 = new Set(array2);
const itemsInArray1Only = array1.filter(el => !set2.has(el))
const itemsInArray2Only = array2.filter(el => !set1.has(el))
return itemsInArray1Only.concat(itemsInArray2Only)
}
/* Return all common items, use set to avoid N squared behaviour */
function getIntersection(array1, array2) {
const set2 = new Set(array2);
return array1.filter(el => set2.has(el));
}
console.log("Intersection:", getIntersection(readFileArray[0], comparePathName))
console.log("Difference:", getSymmetricDifference(readFileArray[0], comparePathName))
One simple way to do it is using array.filter and includes methods:
To get the common values, filter will iterate the first array and includes will check the second array for the current value. To get the difference, check if the current value is not found in the second array.
let availableName = readFileArray[0].filter(value => comparePathName.includes(value))
let expectedArray = comparePathName.filter(value => !readFileArray[0].includes(value))
Of course, to get all different values you will need to filter both ways by switching the arrays in the code above, and then combine the results. Or simply check which array's got more elements and use that for the filter.
Simple example:
let readFileArray = [
[
'mainFolder/abc_doc.txt',
'mainFolder/pqr_doc.txt',
'mainFolder/subFolder/xyz_image.jpg',
'mainFolder/subFolder/iop_pdf.pdf',
],
[
'Fish',
'Life',
'Qwerty',
'Moon'
]
]
let comparePathName = [
'mainFolder/abc_doc.txt',
'mainFolder/pqr_doc.txt',
'mainFolder/subFolder/xyz_image.jpg',
'mainFolder/subFolder/iop_pdf.pdf',
'mainFolder/fish.txt',
'mainFolder/life.txt',
'mainFolder/subFolder/qwerty.jpg',
'mainFolder/subFolder/moon.pdf',
]
let availableName = readFileArray[0].filter(value => comparePathName.includes(value))
let expectedArray = comparePathName.filter(value => !readFileArray[0].includes(value))
console.log('Common', availableName)
console.log('Different', expectedArray)

Combine 2 JSON objects of unequal size with ID

Problem
I would like to have the below two JSON combined together using the ID and have the expected result as mentioned below. I have tried a few solutions that were available but none worked for my use case. Any suggestions will be great !!
Tried to do:
How to merge two json object values by id with plain Javascript (ES6)
Code
var json1 = [
{
"id":"A123",
"cost":"5020.67",
"fruitName":"grapes"
},
{
"id":"A456",
"cost":"341.30",
"fruitName":"apple"
},
{
"id":"A789",
"cost":"3423.04",
"fruitName":"banana"
}
];
var json2 = [
{
"id":"A123",
"quantity":"7"
},
{
"id":"A789",
"quantity":"10"
},
{
"id":"ABCD",
"quantity":"22"
}
];
Below is the code I tried:
var finalResult = [...[json1, json2].reduce((m, a) => (a.forEach(o => m.has(o.id) && Object.assign(m.get(o.id), o) || m.set(o.id, o)), m), new Map).values()];
Expected result:
[
{
"id":"A123",
"cost":"5020.67",
"fruitName":"grapes",
"quantity":"7"
},
{
"id":"A456",
"cost":"341.30",
"fruitName":"apple"
},
{
"id":"A789",
"cost":"3423.04",
"fruitName":"banana",
"quantity":"10"
},
{
"id":"ABCD",
"quantity":"22"
}
]
You can accomplish this fairly easily without getting too fancy. Here's the algorithm:
Put the items from json1 into an object by id, so that you can look them up quickly.
For each item in json2: If it already exists, merge it with the existing item. Else, add it to objectsById.
Convert objectsById back to an array. I've used Object.values, but you can also do this easily with a loop.
var json1 = [
{
"id":"A123",
"cost":"5020.67",
"fruitName":"grapes"
}, {
"id":"A456",
"cost":"341.30",
"fruitName":"apple"
}, {
"id":"A789",
"cost":"3423.04",
"fruitName":"banana"
}
];
var json2 = [
{
"id":"A123",
"quantity":"7"
}, {
"id":"A789",
"quantity":"10"
}
];
const objectsById = {};
// Store json1 objects by id.
for (const obj1 of json1) {
objectsById[obj1.id] = obj1;
}
for (const obj2 of json2) {
const id = obj2.id;
if (objectsById[id]) {
// Object already exists, need to merge.
// Using lodash's merge because it works for deep properties, unlike object.assign.
objectsById[id] = _.merge(objectsById[id], obj2)
} else {
// Object doesn't exist in merged, add it.
objectsById[id] = obj2;
}
}
// All objects have been merged or added. Convert our map to an array.
const mergedArray = Object.values(objectsById);
I think a few steps are being skipped in your reduce function. And it was a little difficult to read because so many steps are being combined in one.
One critical piece that your function does not account for is that when you add 2 numerical strings together, it concats the strings.
const stringTotal = "5020.67" + "3423.04" // result will be "5020.673423.04"
The following functions should give you the result you are looking for.
// calculating the total cost
// default values handles cases where there is no obj in array 2 with the same id as the obj compared in array1
const calcualteStringTotal = (value1 = 0, value2 = 0) => {
const total = parseFloat(value1) + parseFloat(value2)
return `${total}`
}
const calculateTotalById = (array1, array2) => {
const result = []
// looping through initial array
array1.forEach(outterJSON => {
// placeholder json obj - helpful in case we have multiple json in array2 with the same id
let combinedJSON = outterJSON;
// looping through second array
array2.forEach(innerJSON => {
// checking ids
if(innerJSON.id === combinedJSON.id) {
// calls our helper function to calculate cost
const updatedCost = calcualteStringTotal(innerJSON.cost, outterJSON.cost)
// updating other properties
combinedJSON = {
...outterJSON,
...innerJSON,
cost: updatedCost
}
}
})
result.push(combinedJSON)
})
return result
}
const combinedResult = calculateTotalById(json1, json2)
I figured that by using reduce I could make it work.
var finalResult = [...[json1, json2].reduce((m, a) => (a.forEach(o => m.has(o.id) && Object.assign(m.get(o.id), o) || m.set(o.id, o)), m), new Map).values()];

How to group multiple sets of duplicate integers in an array into their own array of arrays?

I am trying to split an array of integers into an array of arrays by duplicate values. The original array is composed of a list of 6 digit integers, some of these integers come in pairs, others come in groups of 3 or 4s. I'd like to get these duplicates pushed to their own arrays and have all of these arrays of duplicates composed into an array of arrays that I can later loop through.
I've looked on in the lodash library for some method or combination of but can't quite find anything that seems to work. I've also tried a few different configurations with nested for loops but also am struggling with that.
const directory = "X/";
let files = fs.readdirSync(directory);
let first6Array = [ ];
for(i=0; i< files.length; i++){
let first6 = files[i].substring(0, 6);
first6Array.push(first6);
};
console.log(first6Array);
example output of first6Array:
[ '141848',
'141848',
'141848',
'142851',
'142851',
'143275',
'143275']
I'd like to end up with something like
let MasterArray = [[141848,141848,141848],[142851,142851],[143275,143275]];
You can use new Set() to filter out the duplicates.
Then you use the unique Array and filter for every value.
const firstArray = [ '141848', '141848', '141848', '142851', '142851', '143275', '143275'];
const numberArray = firstArray.map(Number);
const masterArray = [];
const unique = new Set (numberArray); // Set {141848, 142851, 143275}
unique.forEach(u => {
masterArray.push(numberArray.filter(e => e === u));
});
console.log(masterArray);
Using lodash, you can create a function with flow:
map the items by truncating them and converting to numbers.
groupBy the value (the default).
convert to an array of arrays using values.
const { flow, partialRight: pr, map, truncate, groupBy, values } = _;
const truncate6 = s => truncate(s, { length: 6, omission: '' });
const fn = flow(
pr(map, flow(truncate6, Number)),
groupBy,
values,
);
const firstArray = [ '141848abc', '141848efg', '141848hij', '142851klm', '142851opq', '143275rst', '143275uvw'];
const result = fn(firstArray);
console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script>
Use reduce to create an object of arrays, indexed by number, and push to the associated array on each iteration (creating the array at the key first if needed), then get the values of the object:
const directory = "X/";
const files = fs.readdirSync(directory);
const output = Object.values(
files.reduce((a, file) => {
const num = Number(file.slice(0, 6));
if (!a[num]) a[num] = [];
a[num].push(num);
return a;
}, {})
);
It's pretty weird to have an array of identical values, though - you might consider a different data structure like
{
'141848': 3,
'142851': 2
}
to keep track of the number of occurrences of each number:
const output = files.reduce((a, file) => {
const num = file.slice(0, 6);
a[num] = (a[num] || 0) + 1;
return a;
}, {})
To obtain exactly the result you desire, you need a nested find, something like this should works:
const directory = "X/";
let files = fs.readdirSync(directory);
let first6Array = files.reduce((acc, value)=> {
let n = +value.substr(0, 6); // assumes it can't be NaN
let arr = acc.find(nested => nested.find(item => item === n));
if (arr) {
arr.push(n);
} else {
acc.push([n]);
}
return acc;
}, []);
console.log(first6Array);
Notice that an hashmap instead, with the value and the number of occurrence, would be better, also in term of performance, but I don't think it mind since you have really few elements.
Also, it assumes the first six characters are actually numbers, otherwise the conversion would fail and you'll get NaN.
It would be safer adding a check to skip this scenario:
let n = +value.substr(0, 6);
if (isNaN(n)) {
return acc;
}
// etc

Categories