Map 3 arrays of objects by key in JavaScript - javascript

I have 3 sets of objects, 1 Set can be map to a corresponding set by a unique key. That same set can be mapped to the 3rd set by a different key. I need to be able to map all of these into a new combined set. These sets all have different properties.
Unique Count [users][sector]
Many Count [invoices]
Each unique [user] belongs to a specific [sector] found by (comitID), that same [user] can have many [invoices] though. A one to many field if your familiar with Relational Databases
const users= [ // Unique Entries
{name:'user1', comitId: 'aa1'},
{name:'user2', comitId: 'aa2'}
]
const sector= [ // Unique Entries
{comitID: 'aa1', department: 'finance'},
{comitID: 'aa2', department: 'marketing'},
{comitID: 'aa3', department: 'engineering'}
]
const invoices= [ // Multiple Entries
{name: 'user1' : statementDate: '2/1/2019'},
{name: 'user1' : statementDate: '2/14/2019'},
{name: 'user2' : statementDate: '2/1/2019'}
]
The new set should look like this. Cannot contain a list for the statement dates, they each need to be a new object.
const results = [
{name: 'user1', comitId: 'aa1', department: 'finance', statementDate: '2/1/2019'},
{name: 'user1', comitId: 'aa1', department: 'finance', statementDate: '2/14/2019'},
{name: 'user2', comitId: 'aa2', department: 'marketing', statementDate: '2/1/2019'}
]
I have been trying this in Excel with vlookups and formulas. These files tend to be 10k for the unique counts and up 40k for the invoices.

You can use Array.map() over invoices and Array.find() to get the corresponding entries in users and then sectors:
const users = [ // Unique Entries
{name:'user1', comitId: 'aa1'},
{name:'user2', comitId: 'aa2'}
];
const sectors = [ // Unique Entries
{comitID: 'aa1', department: 'finance'},
{comitID: 'aa2', department: 'marketing'},
{comitID: 'aa3', department: 'engineering'}
];
const invoices = [ // Multiple Entries
{name: 'user1', statementDate: '2/1/2019'},
{name: 'user1', statementDate: '2/14/2019'},
{name: 'user2', statementDate: '2/1/2019'}
];
const result = invoices.map(invoice => {
const user = users.find(u => u.name === invoice.name) || {};
const sector = sectors.find(s => s.comitID === user.comitId) || {};
return { ...invoice, ...sector };
});
console.log(result);

I would suggest that you just iterate over the invoices and enrich the entries with the ones from the sets with unique entries.
Something like this (does not tested the code, but I hope you can understand the idea)
const data = invoices
.map(entry => {...entry, ...{
users.find(user => user.name === entry.name)
.map(user => {...user,
sector.find(sec=> sec.comitID === user.commitID)
})
}}
)
You could improve speed when you first create a map out of the sets and then just lookup the join attributes instead of searching for them
const userMap = users.reduce((map, user) => {...map, ...{user.name: user}}, {})
const sectorMap = sector.reduce((map, sec) => {...map, ...{sector.comitID: sec}}), {})
const data = invoices.map(invoice => {...invoice, ...userMap[invoice.name], ...sector[userMap[invoice.name].comitID]})

Here's a basic script that could work.
const users = [ // Unique Entries
{name:'user1', comitId: 'aa1'},
{name:'user2', comitId: 'aa2'}
]
const sectors = [ // Unique Entries
{comitID: 'aa1', department: 'finance'},
{comitID: 'aa2', department: 'marketing'},
{comitID: 'aa3', department: 'engineering'}
]
const invoices = [ // Multiple Entries
{name: 'user1', statementDate: '2/1/2019'},
{name: 'user1', statementDate: '2/14/2019'},
{name: 'user2', statementDate: '2/1/2019'}
]
sectors.forEach(sector => {
const user = users.find(user => sector.comitID === user.comitId);
if (user) {
user.department = sector.department;
}
});
const results = invoices.map(invoice => {
const user = users.find(user => invoice.name === user.name);
return Object.assign({}, user, { statementDate: invoice.statementDate });
});
console.log(results);

You can use map & filter
const users = [ // Unique Entries
{
name: 'user1',
comitId: 'aa1'
},
{
name: 'user2',
comitId: 'aa2'
}
]
const sector = [ // Unique Entries
{
comitID: 'aa1',
department: 'finance'
},
{
comitID: 'aa2',
department: 'marketing'
},
{
comitID: 'aa3',
department: 'engineering'
}
]
const invoices = [ // Multiple Entries
{
name: 'user1',
statementDate: '2/1/2019'
},
{
name: 'user1',
statementDate: '2/14/2019'
},
{
name: 'user2',
statementDate: '2/1/2019'
}
]
let newArray = invoices.map(function(item) {
// this value will be use to find match between users & sectors
let cId = users.filter(user => user.name === item.name)[0].comitId;
return {
name: item.name,
statementDate: item.statementDate,
comitId: cId,
department: sector.filter(sector => sector.comitID === cId)[0].department
}
});
console.log(newArray)

You could move user and sector items into a map and take this object, if necessary.
const
users = [{ name: 'user1', comitId: 'aa1' }, { name: 'user2', comitId: 'aa2' }],
sector = [{ comitID: 'aa1', department: 'finance' }, { comitID: 'aa2', department: 'marketing' }, { comitID: 'aa3', department: 'engineering' }],
invoices = [{ name: 'user1', statementDate: '2/1/2019'}, { name: 'user1', statementDate: '2/14/2019' }, { name: 'user2', statementDate: '2/1/2019' }],
setMap = k => (m, o) => m.set(o[k], o),
userMap = users.reduce(setMap('name'), new Map),
sectorMap = sector.reduce(setMap('comitID'), new Map),
result = invoices.map(({ name, statementDate }) => {
var { comitId } = userMap.get(name),
{ department } = sectorMap.get(comitId);
return { name, comitId, department, statementDate };
});
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Related

How to manipulate object of array with properties to different names and return the result in a single object using JavaScript or ecmascript-6

I have a request data - array of object like below:
const requestData = [
{
name: 'Test1',
address: 'FL',
},
{
name: 'Test2',
address: 'AL',
},
{
name: 'Test3',
address: 'AK',
},
];
I want to manipulate the object properties based on the index (that mean if index 0 change name property to USER_NAME, if index 1 means change to EMP_NAME) and convert it into final object as below:
const finalResult = {
USER_NAME: 'Test1',
USER_ADDRESS: 'FL',
EMP_NAME: 'Test2',
EMP_ADDRESS: 'AL',
CUST_NAME: 'Test3',
CUST_ADDRESS: 'AK',
};
Using reduce() and an extra map array can do it
let data = [{
name: 'Test1',
address: 'FL',
},
{
name: 'Test2',
address: 'AL',
},
{
name: 'Test3',
address: 'AK',
},
]
let mdata =['USER_','EMP_','CUST_']
let result = data.reduce((a,v,i) =>{
let key = mdata[i]
a[key+'NAME'] = v.name
a[key+'ADDRESS'] = v.address
return a
},{})
console.log(result)
You could destructure and construct your final result:
const [ user, emp, cust ] = requestData;
const finalResult = {
USER_NAME: user.name,
USER_ADDRESS: user.address,
EMP_NAME: emp.name,
EMP_ADDRESS: emp.address,
CUST_NAME: cust.name,
CUST_ADDRESS: cust.address
};
Alternatively, you could reduce, but the code winds up being a bit more complicated than is necessary for this particular example:
const keys = [ "USER", "EMP", "CUST" ];
const finalResult = requestData.reduce(( out, { name, address }, index ) => {
out[ `${ keys[ index ] }_NAME` ] = name;
out[ `${ keys[ index ] }_ADDRESS` ] = address;
return out;
}, {});

Convert an array of object to a new array of object with keys in javscript

I have an array of objects in the format below and would like to transform it into a new array of objects using a property as a key. The key should be unique. See shape of the object below
const mockedList = [
{
email: 'aaa#example.com',
id: '5052',
name: 'Java',
},
{
email: 'bbb#example.com',
id: '5053',
name: 'Python',
},
{
email: 'aaa#example.com',
id: '5054',
name: 'C#',
},
{
email: 'bbb#example.com',
id: '5055',
name: 'Javascript',
},
];
I would like to transform this and get an array of objects with keys and values in this format.
[
{
email: 'bbb#example.com',
languages: [
{
email: 'bbb#example.com',
id: '5055',
name: 'Javascript',
},
{
email: 'bbb#example.com',
id: '5053',
name: 'Python',
},
]
},
{
email: 'aaa#example.com',
languages: [
{
email: 'aaa#example.com',
id: '5052',
name: 'Java',
},
{
email: 'aaa#example.com',
id: '5054',
name: 'C#',
},
]
}
]
I've tried using map-reduce
const result = mockedList.reduce((r, a) => {
r[a.email] = r[a.email] || [];
r[a.email].push(a);
return r;
}, Object.create(null));
But did not get the right shape of data
You can do:
const mockedList = [{email: 'aaa#example.com',id: '5052',name: 'Java',},{email: 'bbb#example.com',id: '5053',name: 'Python',},{email: 'aaa#example.com',id: '5054',name: 'C#',},{ email: 'bbb#example.com', id: '5055', name: 'Javascript' },]
const mockedListHash = mockedList.reduce((a, c) => {
a[c.email] = a[c.email] || { email: c.email, languages: [] }
a[c.email].languages.push(c)
return a
}, {})
const result = Object.values(mockedListHash)
console.log(result)
In case you want to clean the repeated emails within languages:
const mockedList = [{email: 'aaa#example.com',id: '5052',name: 'Java',},{email: 'bbb#example.com',id: '5053',name: 'Python',},{email: 'aaa#example.com',id: '5054',name: 'C#',},{ email: 'bbb#example.com', id: '5055', name: 'Javascript' },]
const mockedListHash = mockedList.reduce((a, c) => {
a[c.email] = a[c.email] || { email: c.email, languages: [] }
a[c.email].languages.push({
id: c.id,
name: c.name,
})
return a
}, {})
const result = Object.values(mockedListHash)
console.log(result)
Here is another option with simple for loop
// Array
const mockedList = [
{
email: 'aaa#example.com',
id: '5052',
name: 'Java'
},
{
email: 'bbb#example.com',
id: '5053',
name: 'Python'
},
{
email: 'aaa#example.com',
id: '5054',
name: 'C#'
},
{
email: 'bbb#example.com',
id: '5055',
name: 'Javascript'
}
];
// Set new object
const newObj = {};
// Use regular loop
for(const el of mockedList) {
// Use email as key
// If key already exist, add info
// to it's languages array
if(newObj[el.email]) newObj[el.email].languages.push(el);
else newObj[el.email] = {
email: el.email,
languages: [el]
}
}
// Test
console.log(newObj);
// If you need just array of objects,
// without email as key, then transform it
const newArr = Object.keys(newObj).map((key) => newObj[key]);
// Test
console.log(newArr);

pushing new objects to array results in mutation of existing object

I am trying to create a .forEach() in which for every user object in users, certain properties of user are mapped then pushed to players array as new objects apart from eachother.
However in my attempt, all the results of user stack into one object. How can I make sure each .push() creates a new object for each user
What I Want
[
{
name: 'Johnny',
id: '123456789',
lives: 3
},
{
name: 'Timmy',
id: '987654321',
lives: 3
},
]
What I Get
[
{
name: [ 'Johnny', 'Timmy' ],
id: [ '123456789', '987654321' ],
lives: 3
}
]
Code
let players = []
const users = {...}
users.forEach(user => {
let username = user.map(user => user.username)
let id = user.map(user => user.id)
players.push({ name: username, id: id, lives: 3 })
})
console.log(players)
You can accomplish this with a simple map call. I think you want something more like:
const users = [
{ username: 'alice', id: 123 },
{ username: 'bob', id: 456 },
]
const players = users.map(user => ({
name: user.username,
id: user.id,
lives: 3
}))
console.log(players);

How to create a new array of objects form object without duplicates ? (ES6)

I would like to create an array of all "department" from the "users" array without duplicate in ES6.
I've tried with forEach, reduce, filter, without success...
Users array:
let users = [{
firstname: 'test',
department: {
id: 1,
name: 'hello'
}
},
{
firstname: 'test2',
department: {
id: 2,
name: 'hello2'
}
},
{
firstname: 'test2',
department: {
id: 1,
name: 'hello'
}
}
]
Result expected:
// Expected
departments = [{
id: 1,
name: 'hello'
},
{
id: 2,
name: 'hello2'
}
] */
My own experiment:
let departments = []
users.forEach(user => {
console.log('-------------------')
console.log(departments)
console.log(user)
console.log(user.department)
console.log(departments.includes(user.department))
if (!departments.includes(user.department)) {
departments.push(user.department)
}
console.log(departments)
})
console.log(departments)
Thanks for your help!
Problem:
Your problem is that you are checking for departments with Array#includes() which is rather used with primitives such as Number and string and doesn't compare objects, try not to use it as it's not compatible with IE also.
Solution:
You can do it using Array#map() and Array#filter() methods:
var deps = users.map(u => u.department);
let results = deps.filter((item, pos) => {
return deps.map(v => v.id).indexOf(item.id) == pos;
});
First map the items to keep only the department object.
Then filter the departments to exclude the ones that has the same id.
Demo:
This is a working demo:
let users = [{
firstname: 'test',
department: {
id: 1,
name: 'hello'
}
},
{
firstname: 'test2',
department: {
id: 2,
name: 'hello2'
}
},
{
firstname: 'test2',
department: {
id: 1,
name: 'hello'
}
}
];
var deps = users.map(u => u.department);
let results = deps.filter((item, pos) => {
return deps.map(v => v.id).indexOf(item.id) == pos;
});
console.log(results);
Just map to the departments, then filter out based on the id:
const ids = new Set;
const result = users
.map(user => user.department)
.filter(({ id }) => !ids.has(id) && ids.add(id));
(This is O(n) as Set lookup / insertion is O(1))
You can use Array.reduce() for that:
let users = [{
firstname: 'test',
department: {
id: 1,
name: 'hello'
}
},
{
firstname: 'test2',
department: {
id: 2,
name: 'hello2'
}
},
{
firstname: 'test2',
department: {
id: 1,
name: 'hello'
}
}
];
let departments = users.reduce((acc, obj)=>{
let exist = acc.find(({id}) => id === obj.department.id);
if(!exist){
acc.push({id:obj.department.id, name: obj.department.name});
}
return acc;
}, []);
console.log(departments);

javascript map two nested arrays and modify the existing by lookups

I have a kids object that looks like the following:
const kids = {
name: 'john',
extra: {
city: 'London',
hobbies: [
{
id: 'football',
team: 'ABC',
},
{
id: 'basketball',
team: 'DEF',
},
],
},
};
and i have the following object that contains all sports and extra info for each.
const sports = [
{
name: 'volleyball',
coach: 'tom',
},
{
name: 'waterpolo',
coach: 'jack',
},
{
name: 'swimming',
coach: 'kate',
},
{
name: 'football',
coach: 'sara',
},
];
I want to get the list of all ids in the hobbies array and go through each of the sports items in the sports array, and found, add an extra field to that object available and give a value of true, so the result will look like:
const result = [
{
name: 'volleyball',
coach: 'tom',
},
{
name: 'waterpolo',
coach: 'jack',
},
{
name: 'swimming',
coach: 'kate',
},
{
name: 'football',
coach: 'sara',
available: true
},
];
by the way, here is my attempt:
const result = kids.extra.hobbies.map(a => a.id);
for (var key in sports) {
console.log(sports[key].name);
const foundIndex = result.indexOf(sports[key].name);
if ( foundIndex > -1) {
sports[key].available = true;
}
}
console.log(sports)
but this is too long... i am looking one liner looking code and robust logic.
This can be done many ways; however, an easy was is to divide the problem into two steps:
We can first flatten the kid's hobbies into an array by using the Array.map() function:
const hobbies = kids.extra.hobbies.map(hobby => hobby.id);
Then, we can iterate through the sports array and add an active property to any object which is present in the new hobbies array:
const result = sports.map(sport => {
if (hobbies.indexOf(sport.name) !== -1) {
sport.available = true;
}
return sport;
})
Complete Solution
const kids = {
name: 'john',
extra: {
city: 'London',
hobbies: [{
id: 'football',
team: 'ABC',
},
{
id: 'basketball',
team: 'DEF',
},
],
},
};
const sports = [{
name: 'volleyball',
coach: 'tom',
},
{
name: 'waterpolo',
coach: 'jack',
},
{
name: 'swimming',
coach: 'kate',
},
{
name: 'football',
coach: 'sara',
},
];
const hobbies = kids.extra.hobbies.map(hobby => hobby.id);
const result = sports.map(sport => {
if (hobbies.indexOf(sport.name) !== -1) {
sport.available = true;
}
return sport;
})
console.log(result);
Firstly, I would change my data structures to objects. Any time you have a list of things with unique ids, objects will make your life much easier than arrays. With that in mind, if you must use arrays, you could do the following:
const hobbies = kids.extra.hobbies
sports.forEach(s => s.available = hobbies.some(h => h.id === s.name))
Note that this mutates the original sports object (change to map for new), and also adds false/true instead of just true.
Build an array of the found sports first, then map while checking to see if the sports object's name is in it:
const kids = {name:'john',extra:{city:'London',hobbies:[{id:'football',team:'ABC',},{id:'basketball',team:'DEF',},],},}
const sports = [{name:'volleyball',coach:'tom',},{name:'waterpolo',coach:'jack',},{name:'swimming',coach:'kate',},{name:'football',coach:'sara',},];
const sportsInHobbies = kids.extra.hobbies.map(({ id }) => id);
const result = sports.map((sportObj) => {
const available = sportsInHobbies.includes(sportObj.name);
return available ? {...sportObj, available } : { ...sportObj };
});
console.log(result);

Categories