Merge two array of objects based on matching properties - javascript

I've 2 arrays with partial information and I wish to merge those arrays with all the information into one array.
Array 1 :
const arr1 = [
{
name: 'Rohan',
surname: 'Mehra',
age: '15',
date: "2021-01-19",
location: 'Goa'
},
{
name: 'Aman',
surname: 'Kohli',
age: '14',
date: "2021-01-19",
location: 'Kolkata'
},
{
name: 'Sam',
surname: 'Sharma',
age: '16',
date: "2021-01-21",
location: 'Mumbai'
}
]
Array 2 :
const arr2 = [
{
rollNo: 1,
marks: 100,
name: 'Rohan',
date: "2021-01-19",
},
{
rollNo: 2,
marks: 90,
surname: 'Kohli',
date: "2021-01-19",
},
{
rollNo: 3,
marks: 70,
date: "2021-01-21",
ExamCenter: {
place: 'Mumbai'
}
}
]
I want to get a final array with the properties from both arrays. The Object keys sometimes change and I wanted to match the key with the other common key and merge them. But I am not able to proceed with the solution. Here is the result array I wish to get.
const final = [
{
name: 'Rohan',
surname: 'Mehra',
age: '15',
date: "2021-01-19",
location: 'Goa',
rollNo: 1,
marks: 100,
},
{
name: 'Aman',
surname: 'Kohli',
age: '14',
date: "2021-01-19",
location: 'Kolkata',
rollNo: 2,
marks: 90,
},
{
name: 'Sam',
surname: 'Sharma',
age: '16',
date: "2021-01-21",
location: 'Mumbai',
rollNo: 3,
marks: 70,
}
]
I'm trying with nested map loops but not able to proceed
const final = arr1.map((item,index) => {
arr2.map((innerItem, i) => {
if(item[Object.keys(innerItem)][index] === innerItem[Object.keys(innerItem)][0]){
console.log(item);
}
})
})

There is a mistake in your arr2. The surname for 2nd item should be kohli instead of kolhi. Anyway, You can do the following to merge two array based on dynamic matching attribute. What we are doing here is,
For each item of arr1 we are finding the keys using Object.keys method and checking which object from arr2 has maximum matching object with the item of arr1. Then we merge the two item together.
arr1 = [
{
name: 'Rohan',
surname: 'Mehra',
age: '15',
date: "2021-01-19",
location: 'Goa'
},
{
name: 'Aman',
surname: 'Kohli',
age: '14',
date: "2021-01-19",
location: 'Kolkata'
},
{
name: 'Sam',
surname: 'Sharma',
age: '16',
date: "2021-01-21",
location: 'Mumbai'
}
]
arr2 = [
{
rollNo: 1,
marks: 100,
name: 'Rohan',
date: "2021-01-19",
},
{
rollNo: 2,
marks: 90,
surname: 'Kohli',
date: "2021-01-19",
},
{
rollNo: 3,
marks: 70,
date: "2021-01-21",
ExamCenter: {
place: 'Mumbai'
}
}
]
res = arr1.map(item => {
keys1 = Object.keys(item);
let max = 0;
const temp = arr2.reduce((prev, item2) => {
maxTemp = keys1.filter(key => item[key] === item2[key]).length;
if(maxTemp > max) {
max = maxTemp;
prev = item2;
}
return prev;
}, {})
if(temp) {
return {...item, ...temp}
}
});
console.log(res);

You can do something like this to merge two arrays.
const arr1 = [
{
name: 'Rohan',
surname: 'Mehra',
age: '15',
date: "2021-01-19",
location: 'Goa'
},
{
name: 'Aman',
surname: 'Kohli',
age: '14',
date: "2021-01-19",
location: 'Kolkata'
},
{
name: 'Sam',
surname: 'Sharma',
age: '16',
date: "2021-01-21",
location: 'Mumbai'
}
]
const arr2 = [
{
rollNo: 1,
marks: 100,
name: 'Rohan',
date: "2021-01-19",
},
{
rollNo: 2,
marks: 90,
surname: 'Kolhi',
date: "2021-01-19",
},
{
rollNo: 3,
marks: 70,
date: "2021-01-21",
ExamCenter: {
place: 'Mumbai'
}
}
]
const newArray = [];
arr2.forEach((item) => {
const array1Item = arr1.find(({ date }) => date === item.date);
if (array1Item) {
newArray.push({
...item,
...array1Item,
})
}
})
console.log(newArray);

This may help you
const arr3 = arr1.map((value, index) => {
return Object.assign(value, arr2[index])
})

Related

JS - merge 2 arrays based on properties

I'm trying to merge 2 arrays of objects where the resulting array should have only objects present in array 1 (selectedNames) but with one property from corresponding objects from array 2 (nameDetails). There's one matching (unique) property to match them:
const selectedNames = [
{
id: '11'
name: 'joe',
},
{
id: '22',
name: 'bill',
},
];
const nameDetails = [
{
nameId: '11',
salary: '23422',
location: 'New Jersey',
},
{
nameId: '33',
salary: '23424',
location: 'New York',
},
{
nameId: '22',
salary: '99999',
location: 'Boston',
},
{ nameId: '44',
salary: '323232',
location: 'Chicago',
},
];
The matching property is selectedNames.id === nameDetails.nameId. All entries in selectedNames will definitely be present in nameDetails (but not the other way round). The resulting array should look like that:
[
{
id: '11',
name: 'joe',
salary: '23422',
},
{
id: '22',
name: 'bill',
salary: '99999'
}
]
I'm a bit confused. I know it'll probably consist of .includes() and filter() and ... for merging? I'm not sure how to handle it.
Alternatively, which will probably be much easier, filter the nameDetails array to have only objects with nameId that exists (as id) in selectedNames.
I am a bit confused by your example result. For example where does id: '11111' come from?
Are you looking for something like this maybe?
const selectedNames = [
{
id: '11',
name: 'joe',
},
{
id: '22',
name: 'bill',
},
];
const nameDetails = [
{
nameId: '11',
salary: '23422',
location: 'New Jersey',
},
{
nameId: '33',
salary: '23424',
location: 'New York',
},
{
nameId: '22',
salary: '99999',
location: 'Boston',
},
{ nameId: '44',
salary: '323232',
location: 'Chicago',
},
];
const merged = selectedNames.map(n => {
n.salary = nameDetails.filter(d => n.id === d.nameId)[0].salary;
return n;
});
console.log(merged)
Note: This will change the original selectedNames by adding the salary from the corresponding entry in nameDetails.
Maybe this fits your needs:
selectedNames.map(({ id, name }) => {
let salary = nameDetails.find((nameItem) => nameItem.nameId === id).salary;
return { id, name, salary };
})
Will result in:
[
{
id: '11',
name: 'joe',
salary: '23422',
},
{
id: '22',
name: 'bill',
salary: '99999',
}
]

Why is my condition that checks diff between two arrays of objects wrong?

What is wrong in this if condition. I am getting the wrong result. I need to get equal values in these two objects and diff between them.
const firstArr = [{ name: 'tom', age: 22, city: 'Madrid' }, { name: 'Alex', age: 23, city: 'Berlin' }, { name: 'Sara', age: 28, city: 'Paris' }, { name: 'Rash', age: 20, city: 'Dubai' } ];
const secondArr = [{ name: 'tom', age: 22, city: 'Madrid' }, { name: 'Alex', age: 27, city: 'Berlin' }, { name: 'Hary', age: 29, city: 'London' }, ];
for (let i = 0; i < firstArr.length; i++) {
for (let j = 0; j < secondArr.length; j++) {
if (firstArr[i].name == secondArr[j].name) {
console.log('eq', firstArr[i].city, secondArr[j].city)
}
if (firstArr[i].name != secondArr[j].name) {
console.log('not found in second array', firstArr[i].city)
}
if (secondArr[j].name != firstArr[i].name) {
console.log('not found in first array', secondArr[j].city)
}
}
}
Currently you compare each element of the first array with each element of the second array. You could instead use Array.prototype.some and Array.prototype.every to filter the arrays and to find the intersection resp. difference. Then you can map the objects to the city names.
const firstArr = [{ name: 'tom', age: 22, city: 'Madrid' }, { name: 'Alex', age: 23, city: 'Berlin' }, { name: 'Sara', age: 28, city: 'Paris' }, { name: 'Rash', age: 20, city: 'Dubai' } ];
const secondArr = [{ name: 'tom', age: 22, city: 'Madrid' }, { name: 'Alex', age: 27, city: 'Berlin' }, { name: 'Hary', age: 29, city: 'London' }, ];
function intersect(lhs, rhs) {
return lhs.filter(el => rhs.some(el2 => el.name === el2.name)).map(el => el.city);
}
function diff(lhs, rhs) {
return lhs.filter(el => rhs.every(el2 => el.name !== el2.name)).map(el => el.city);
}
console.log(intersect(firstArr, secondArr));
console.log(diff(firstArr, secondArr));
console.log(diff(secondArr, firstArr));
I loop the first array first and find matches in second array. If there is a match, diff is displayed. If there is no such match, then the correct text is being displayed. An array is built along the way, which is used to simplify the loop on the second array.
const firstArr = [{ name: 'tom', age: 22, city: 'Madrid' }, { name: 'Alex', age: 23, city: 'Berlin' }, { name: 'Sara', age: 28, city: 'Paris' }, { name: 'Rash', age: 20, city: 'Dubai' } ];
const secondArr = [{ name: 'tom', age: 22, city: 'Madrid' }, { name: 'Alex', age: 27, city: 'Berlin' }, { name: 'Hary', age: 29, city: 'London' }, ];
let names = [];
for (let first of firstArr) {
let matches = secondArr.filter((second) => (first.name === second.name));
if (matches.length) {
console.log('eq', first.city, matches[0].city)
} else {
console.log('not found in second array', first.city);
}
names.push(first.name);
}
for (let second of secondArr) {
if (names.indexOf(second.name) === -1) console.log('not found in first array', second.city);
}
Try this:
const firstArr = [{ name: 'tom', age: 22, city: 'Madrid' }, { name: 'Alex', age: 23, city: 'Berlin' }, { name: 'Sara', age: 28, city: 'Paris' }, { name: 'Rash', age: 20, city: 'Dubai' } ];
const secondArr = [{ name: 'tom', age: 22, city: 'Madrid' }, { name: 'Alex', age: 27, city: 'Berlin' }, { name: 'Hary', age: 29, city: 'London' }, ];
var eq = [], uniqueInFirst = [], uniqueInSecond = [];
for (let i = 0; i < firstArr.length; i++) {
var secondArrCities = Object.values(secondArr).map ((obj) => {return obj.city})
if (secondArrCities.includes(firstArr[i].city)) {
eq.push(firstArr[i].city)
} else {
uniqueInFirst.push(firstArr[i].city)
}
}
for (let i = 0; i < secondArr.length; i++) {
var firstArrCities = Object.values(firstArr).map ((obj) => {return obj.city})
if (!firstArrCities.includes(secondArr[i].city)) {
uniqueInSecond.push(secondArr[i].city)
}
}
console.log(eq)
console.log(uniqueInFirst)
console.log(uniqueInSecond)

How can I regroup Array of objects by given field

I have given array of objects, something like this
const data = [
{id: 1, name: 'Alex', job: 'IT'},
{id: 2, name: 'Pavel', job: 'IT'},
{id: 3, name: 'Joe', job: 'IT'},
{id: 4, name: 'Josh', job: 'IT'},
{id: 5, name: 'Max', job: 'teacher'},
{id: 6, name: 'Sam', job: 'teacher'}
]
I need array of arrays filtered by field job
const result = [
{job: 'IT',
workersInfo: [
{id:1, name:'Alex'},
{id:2, name:'Pavel'},
{id:3, name:'Joe'},
{id:4, name:'Josh'}
]
},
{job: 'teacher',
workersInfo: [
{id:5, name: 'Max'},
{id:6, name: 'Sam'}
]
}
]
I tried this, but It's not what I want
const data = [
{id: 1, name: 'Alex', job: 'IT'},
{id: 2, name: 'Pavel', job: 'IT'},
{id: 3, name: 'Joe', job: 'IT'},
{id: 4, name: 'Josh', job: 'IT'},
{id: 5, name: 'Max', job: 'teacher'},
{id: 6, name: 'Sam', job: 'teacher'}
]
const groupList = data.reduce((reduce, it) => {
reduce[it.job] = reduce[it.job] || [];
reduce[it.job].push({id: it.id, name: it.name});
return reduce;
}, {})
console.log(Object.values(groupList));
How can I add new key workers Info and push info to this field
If you create a new object on each iteration instead of an array you can then use Object.values:
const data = [
{id: 1, name: 'Alex', job: 'IT'},
{id: 2, name: 'Pavel', job: 'IT'},
{id: 3, name: 'Joe', job: 'IT'},
{id: 4, name: 'Josh', job: 'IT'},
{id: 5, name: 'Max', job: 'teacher'},
{id: 6, name: 'Sam', job: 'teacher'}
];
const groupList = data.reduce((acc, { job, id, name }) => {
acc[job] = acc[job] || { job, workersInfo: [] };
acc[job].workersInfo.push({ id, name });
return acc;
}, {})
console.log(Object.values(groupList));
Example below
const data = [
{ id: 1, name: "Alex", job: "IT" },
{ id: 2, name: "Pavel", job: "IT" },
{ id: 3, name: "Joe", job: "IT" },
{ id: 4, name: "Josh", job: "IT" },
{ id: 5, name: "Max", job: "teacher" },
{ id: 6, name: "Sam", job: "teacher" },
];
const output = data.reduce((acc, o) => {
const index = acc.findIndex(a => a.job === o.job);
if (index !== -1) {
acc[index].workersInfo.push({ id: o.id, name: o.name });
} else {
acc.push({
job: o.job,
workersInfo: [{ id: o.id, name: o.name }],
});
}
return acc;
}, []);
console.log(output);
Would something like this work ?
const groupBy = function(xs, key) {
return xs.reduce(function(rv, x) {
(rv[x[key]] = rv[x[key]] || []).push(x);
return rv;
}, {});
};
console.log(groupBy(['one', 'two', 'three'], 'length'));
// => {3: ["one", "two"], 5: ["three"]}```
It would be more efficient and comprehensible if instead of having a structure like Array<{job: string, workForce: Array}>, you had something like {[job: string]: Array}
var data = [
{ id: 1, name: 'Alex', job: 'IT' },
{ id: 2, name: 'Pavel', job: 'IT' },
{ id: 3, name: 'Joe', job: 'IT' },
{ id: 4, name: 'Josh', job: 'IT' },
{ id: 5, name: 'Max', job: 'teacher' },
{ id: 6, name: 'Sam', job: 'teacher' }
];
var jobs = data.reduce(function (result, person) {
var jobList = result[person.job];
if (!jobList) {
jobList = [];
result[person.job] = jobList;
}
jobList.push(person);
return result;
}, {});
console.log(jobs);

Making occurrence of object in array a property

I'm trying to add a new property to multiple objects in a array that counts its occurrence. The current function can delete duplicated objects but cannot count how many times it is repeated.
const artists = [
{ name: 'Paul', age: '18', 'id': 1 },
{ name: 'Joseph', age: '21', 'id': 2 },
{ name: 'Maggie', age: '20', 'id': 3 },
{ name: 'Paul', age: '18', 'id': 1 },
{ name: 'Maggie', age: '20', 'id': 3 },
{ name: 'John', age: '18', 'id': 4 },
{ name: 'Joseph', age: '21', 'id': 2 },
{ name: 'Tenner', age: '30', 'id': 5 },
{ name: 'Maggie', age: '20', 'id': 3 },
]
const countOccurrence = (arr) => {
let uniqueArray = []
uniqueArray = arr.filter(item => {
item['occurrence'] = ++item['occurrence'] || 1
if (!uniqueArray.includes(item.id)) {
uniqueArray.push(item.id)
return true
}
})
return uniqueArray
}
let uniqueArtists = countOccurrence(artists)
console.log(uniqueArtists)
I'm getting:
[
{ name: 'Paul', age: '18', id: 1, occurrence: 1 },
{ name: 'Joseph', age: '21', id: 2, occurrence: 1 },
{ name: 'Maggie', age: '20', id: 3, occurrence: 1 },
{ name: 'John', age: '18', id: 4, occurrence: 1 },
{ name: 'Tenner', age: '30', id: 5, occurrence: 1 }
]
I'm trying to get:
[
{ name: 'Paul', age: '18', id: 1, occurrence: 2 },
{ name: 'Joseph', age: '21', id: 2, occurrence: 2 },
{ name: 'Maggie', age: '20', id: 3, occurrence: 3 },
{ name: 'John', age: '18', id: 4, occurrence: 1 },
{ name: 'Tenner', age: '30', id: 5, occurrence: 1 }
]
The order does not matter, I'm just trying to get the right occurrence values.
You could take a hash table and increment the occurence for each grouped worker.
This approach does not mutate the original data.
const
workers = [{ name: 'Paul', age: '18', id: 1 }, { name: 'Joseph', age: '21', id: 2 }, { name: 'Maggie', age: '20', id: 3 }, { name: 'Paul', age: '18', id: 1 }, { name: 'Maggie', age: '20', id: 3 }, { name: 'John', age: '18', id: 4 }, { name: 'Joseph', age: '21', id: 2 }, { name: 'Tenner', age: '30', id: 5 }, { name: 'Maggie', age: '20', id: 3 }],
countOccurrence = (arr) => {
let uniqueArray = [],
hashTable = {}
arr.forEach(item => {
if (!hashTable[item.id]) {
uniqueArray.push(hashTable[item.id] = { ...item, occurrence: 0 });
}
hashTable[item.id].occurrence++;
});
return uniqueArray;
},
uniqueArtists = countOccurrence(workers);
console.log(uniqueArtists);
.as-console-wrapper { max-height: 100% !important; top: 0; }
A slightly different approach by using an object and getting the values as return array.
const
workers = [{ name: 'Paul', age: '18', id: 1 }, { name: 'Joseph', age: '21', id: 2 }, { name: 'Maggie', age: '20', id: 3 }, { name: 'Paul', age: '18', id: 1 }, { name: 'Maggie', age: '20', id: 3 }, { name: 'John', age: '18', id: 4 }, { name: 'Joseph', age: '21', id: 2 }, { name: 'Tenner', age: '30', id: 5 }, { name: 'Maggie', age: '20', id: 3 }],
countOccurrence = (arr) => {
let hashTable = {}
arr.forEach(item => {
if (!hashTable[item.id]) {
hashTable[item.id] = { ...item, occurrence: 0 };
}
hashTable[item.id].occurrence++;
});
return Object.values(hashTable);
},
uniqueArtists = countOccurrence(workers);
console.log(uniqueArtists);
.as-console-wrapper { max-height: 100% !important; top: 0; }
The problem with your approach is that while you update each item in your filter, you're updating only the current iterated value, and then including it in the output (by returning true to filter) only in the case that you haven't already seen its id. So as you go through, you update the occurrence of each one to 1, but then ignore the duplicates. You need to somehow track these by id.
You can do this in a single reduce call, wrapped in Object .values, like this:
const countOccurrences = xs => Object .values (xs .reduce ((
a, x, _i , _arr, // last two unused
{occurrences, ...rest} = x .id in a ? a [x .id] : {...x, occurrences: 0}
) => ({
... a,
[x .id] : {...rest, occurrences: occurrences + 1}
}), {}))
const workers = [ { name: 'Paul', age: '18', 'id': 1 }, { name: 'Joseph', age: '21', 'id': 2 }, { name: 'Maggie', age: '20', 'id': 3 }, { name: 'Paul', age: '18', 'id': 1 }, { name: 'Maggie', age: '20', 'id': 3 }, { name: 'John', age: '18', 'id': 4 }, { name: 'Joseph', age: '21', 'id': 2 }, { name: 'Tenner', age: '30', 'id': 5 }, { name: 'Maggie', age: '20', 'id': 3 } ]
console .log (
countOccurrences (workers)
)
.as-console-wrapper {min-height: 100% !important; top:0}
Note that this does not mutate your original data.
It also does not mutate the accumulator used in the reduce call or its properties. That can occasionally be a performance problem. If your data sets are large or if you're doing this often, you might want to change from this style of immutable accumulator objects to one that mutates the internal accumulator and its values as you go. Rich Snapp has an excellent article describing the reasons for this. But if this performance is acceptable, then I wouldn't bother, as I find this cleaner.

JavaScript group similar objects by same value

I have an array of objects, I need to group them by the same value, I need to group them by a multidimensional array
const source = [
{name: 'A', age: 23, id: 0},
{name: 'A', age: 23, id: 1},
{name: 'B', age: 15, id: 34},
{name: 'B', age: 15, id: 45},
{name: 'B', age: 15, id: 32},
{name: 'C', age: 15, id: 32},
];
[
Like this structure, all the same objects should be grouped by array inside an array.
[
{name: 'A', age: 23, id: 0},
{name: 'A', age: 23, id: 1}
],
[
{name: 'B', age: 15, id: 34},
{name: 'B', age: 15, id: 45},
{name: 'B', age: 15, id: 32}
],
[
{name: 'C', age: 15, id: 32},
]
]
I have tried like this but no luck.
const result = source.reduce((accumulator, item) => {
if (accumulator && accumulator.length) {
const found = accumulator.find(group => {
return group && group.length
? group.find(_transaction =>
// check the same object
return false
)
: false;
});
if (found) {
console.log(found);
}
} else {
accumulator.push([item]);
}
return accumulator;
}, []);
Create an object indexed by the name property, whose values are arrays containing the source items, then take the object's values:
const source = [
{name: 'A', age: 23, id: 0},
{name: 'A', age: 23, id: 1},
{name: 'B', age: 15, id: 34},
{name: 'B', age: 15, id: 45},
{name: 'B', age: 15, id: 32},
{name: 'C', age: 15, id: 32},
];
const sourceItemsByName = {};
for (const obj of source) {
if (!sourceItemsByName[obj.name]) {
sourceItemsByName[obj.name] = [];
}
sourceItemsByName[obj.name].push(obj);
}
const output = Object.values(sourceItemsByName);
console.log(output);
You can reduce the array to a Map, using the name and age combination as the key. Then spread the Map.values() iterator back to an array:
const source = [{ name: 'A', age: 23, id: 0 }, { name: 'A', age: 23, id: 1 }, { name: 'B', age: 15, id: 34 }, { name: 'B', age: 15, id: 45 }, { name: 'B', age: 15, id: 32 }, { name: 'C', age: 15, id: 32 }];
const result = [... // spread the values iterator to an array
source.reduce((r, o) => {
const key = `${o.name}---${o.age}`; // generate a key with name and age
if(!r.has(key)) r.set(key, []); // add a new entry for key if it's not in the Map
r.get(key).push(o); // push the current object to the keys's entry
return r;
}, new Map())
.values() // get the Maps values iterator
];
console.log(result);
I think the following code is easier to understand. Hope, it will help you. Thanks.
UPDATED: As you need mentioned in a comment that you need name and age property as your key value.
const source = [
{name: 'A', age: 23, id: 0},
{name: 'A', age: 23, id: 1},
{name: 'B', age: 15, id: 34},
{name: 'B', age: 15, id: 45},
{name: 'B', age: 15, id: 32},
{name: 'C', age: 15, id: 32},
];
var hashObject = {}
source.forEach(function(elem) {
var key = elem.name + elem.age;
if (hashObject[key]) {
hashObject[key].push(elem);
} else {
hashObject[key] = [elem];
}
});
var desiredArray = Object.values(hashObject);
console.log(desiredArray);
You could find the group array and if found add the object or add a new group to the result set.
const
source = [{ name: 'A', age: 23, id: 0 }, { name: 'A', age: 23, id: 1 }, { name: 'B', age: 15, id: 34 }, { name: 'B', age: 15, id: 45 }, { name: 'B', age: 15, id: 32 }, { name: 'C', age: 15, id: 32 }],
groups = ['name', 'age'],
grouped = source.reduce((r, o) => {
var temp = r.find(([q]) => groups.every(k => o[k] === q[k]));
if (temp) {
temp.push(o);
} else {
r.push([o]);
}
return r;
}, []);
console.log(grouped);
.as-console-wrapper { max-height: 100% !important; top: 0; }
An approach with a Map
const
source = [{ name: 'A', age: 23, id: 0 }, { name: 'A', age: 23, id: 1 }, { name: 'B', age: 15, id: 34 }, { name: 'B', age: 15, id: 45 }, { name: 'B', age: 15, id: 32 }, { name: 'C', age: 15, id: 32 }],
groups = ['name', 'age'],
grouped = Array.from(source
.reduce(
(m, o) =>
(k => m.set(k, [...(m.get(k) || []), o]))
(groups.map(k => o[k]).join('|')),
new Map)
.values()
);
console.log(grouped);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Categories

Resources