pushing new objects to array results in mutation of existing object - javascript

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);

Related

comparing 2 arrays of objects and performing a mongodb bulkwrite with the differences

I am trying to compare an array of sub documents with one array being the current version of the subdocuments and a new array which will be sent by the front end to update the current array of subdocuments with the changes. this array of subdocuments is used to generate unique forms in the long term but have made example code to see if it would work. I generate an array of updates to use with the mongodb bulkwrite feature and need to know if all seems dandy in my code. If there is a more efficient way of doing this, I'm all ears.
This code example is the main function that will be called when the user submits the http put request.
const exampleFunction = async () => {
//create bulkWrite array for mongodb
//compare if the name has changed
//compare if the object does not exist so that it may be pushed into template array.
// EXAMPLE OF PARENT DOCUMENT
// const parentObj = {
// _id: '123',
// //array of sub documents of original document that will be used for comparison
// formData: [
// { _id: 'abc', name: 'Cat' },
// { _id: 'def', name: 'Dog' },
// { _id: 'ghi', name: 'Bird' }
// ]
// }
// ID of parent document
const parentID = '123'
// Current formData array of sub documents in the parent document
const oldArray = [
{ _id: 'abc', name: 'Cat' },
{ _id: 'def', name: 'Dog' },
{ _id: 'ghi', name: 'Bird' }
]
// New array sent from front end to be compared with against the old array
const newArray = [
{ _id: 'abc', name: 'Cat' },
{_id: 'def', name: 'Dog' },
{ _id: 'ghi', name: 'Lizard' },
{ name: 'Goat' }
]
const update = compareArrays(oldArray, newArray, parentID)
// bulkWrite function made to mongodb
await collection.bulkWrite(update).catch((err) => {
console.log(err)
})
}
While this code is the main comparison function that is called in the code block above.
const compareArrays = async (a, b, parentID) => {
let bulkArray = []
a.map((itemA) => {
b.map(async (itemB) => {
if(!itemB._id) {
// Check if a new object has been added that has yet to receive ID from database, if no id, then push to array of parent document
bulkArray.push(
{
updateOne: {
filter: {_id: parentID},
update: { $push: { formData: itemB } },
},
}
)
} else {
if(itemA._id === itemB._id) {
//match ids and perform another check
if (itemA.name !== itemB.name) {
//check if the names do not match and push the update to bulkArray for mongodb to update sub document of parent document
bulkArray.push(
{
updateOne: {
filter: {_id: parentID, 'formData._id': itemA._id},
update: { $set: { name: itemB.name } }
}
}
)
}
}
}
})
})
return bulkArray
}
The final document should look something like this,
const finalParentObj = {
_id: '123',
formData: [
{ _id: 'abc', name: 'Cat' },
{ _id: 'def', name: 'Dog' },
{ _id: 'ghi', name: 'Lizzard' },
{ id: 'jkl', name: 'Goat'}
]
}
So the bulkwrite() is used mainly to reduce the number of multiple trips required to write multiple operations. It does all the operations in an atomic way. It takes the input as an array of operations you wanted to perform on the DB.collection
db.collection.bulkWrite([
{ updateOne: { filter: { a: 1 }, update: { $set: { a: 2 } } } },
{ updateMany: { filter: { b: 1 }, update: { $set: { b: 2 } } } },
]);

Filter json with set and map using nested attributes

I am working with some json returned via an API call and trying to figure if it's possible to filter one json object based on the values from another. I came across set and map which is close, but not sure how to handled the nested attributed... here is a simplified example:
var teachers = [
{id: 1, name : 'Bob',studentid: []},
{id: 2, name : 'Sue',studentid: []},
{id: 3, name : 'Jean',studentid: []},
{id: 4, name : 'Jim',studentid: [
"500zz"
]},
{id: 5, name : 'Paul',studentid: [
"100zz",
"120zz",
"130zz"
]}
];
var students = [
{_id: "100zz", name : 'Betty'},
{_id: "120zz", name : 'Bob'},
{_id: "500zz", name : 'Billy'}
];
console.log(
teachers.filter(
(set => item => set.has(item.studentid))(new Set(students.map(item => item._id)))
)
)
I would like the result to include only the teachers Jim and Paul ( with all associated data )... not sure what I need to add or do? The array of studentid's is throwing me for a loop.
const teachers = [
{ id: 1, name: "Bob", studentid: [] },
{ id: 2, name: "Sue", studentid: [] },
{ id: 3, name: "Jean", studentid: [] },
{ id: 4, name: "Jim", studentid: ["500zz"] },
{ id: 5, name: "Paul", studentid: ["100zz", "120zz", "130zz"] }
];
const students = [
{ _id: "100zz", name: "Betty" },
{ _id: "120zz", name: "Bob" },
{ _id: "500zz", name: "Billy" }
];
// Filter teachers with students greater than 0.
const filtered = teachers.filter(x => x.studentid.length > 0);
// Map teachers with Students
const teachersWithStudents = filtered.map(x => {
const stu = students
.map(student => {
// Determine whether or not teacher 'studentid' contains student '_id'
const hasStudent = x.studentid.includes(student._id);
// Teacher has student
if (hasStudent) {
return {
id: student._id,
name: student.name
};
}
// Teacher does not have student
return false;
})
.filter(x => x); // filter objects that are not false
// Return teacher object with student (stu) objects
return {
...x,
studentid: stu
};
// Return those with matches only
}).filter(x => x.studentid.length > 0);
console.log(teachersWithStudents);

Map 3 arrays of objects by key in 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; }

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