Object manipulation javascript, add value of object to an array of object - javascript

An example will speak for itself :
Array of Object :
[{
userId: 'ab4e3870-287e-11e7-b5a1-abb6183e9866',
email: 'email1#hotmail.com'
},{
userId: 'ae149220-2883-11e7-bbf9-1fb134f2b4ad',
email: 'email2#hotmail.com'
}]
Object
{
'ab4e3870-287e-11e7-b5a1-abb6183e9866': { name: 'john', roles: 'detective'},
'ae149220-2883-11e7-bbf9-1fb134f2b4ad': { name: 'james', roles: 'plumber'},
}
The result i'd like would be :
[{
userId: 'ab4e3870-287e-11e7-b5a1-abb6183e9866',
email: 'email1#hotmail.com',
name: 'john',
roles: 'detective'
},{
userId: 'ae149220-2883-11e7-bbf9-1fb134f2b4ad',
email: 'email2#hotmail.com',
name: 'james',
roles: 'plumber'
}]
So basically, the value of the key that match the userId in Object is added to that object in the array.
Is there some simple way I don't see to do that? Without external libraries in ES6?

var data = [{
userId: 'ab4e3870-287e-11e7-b5a1-abb6183e9866',
email: 'email1#hotmail.com'
},{
userId: 'ae149220-2883-11e7-bbf9-1fb134f2b4ad',
email: 'email2#hotmail.com'
}]
var d = {
'ab4e3870-287e-11e7-b5a1-abb6183e9866': { name: 'john', roles: 'detective'},
'ae149220-2883-11e7-bbf9-1fb134f2b4ad': { name: 'james', roles: 'plumber'},
};
Using ES6 spread operator ...
data = data.map(function(item) {
return {...item, ...d[item.userId]}
});
ES5: By adding properties manually
data = data.map(function(item) {
item.name = d[item.userId].name;
item.roles = d[item.userId].roles;
return item;
});

Related

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

mongoose populate count array field

I want to get objects with populate and field of the array that I want to count.
For Example:
const ChildSchema = new mongoose.Schema({
name: String,
age: Number,
siblings: [{type: ObjectId, ref: 'Child'}],
toys: [{type: ObjectId, ref: 'Toy'}]
})
and I want to get a populate of the siblings and count of the toys in one object, like this:
const Dan = {
name: "Dan",
age: 4,
siblings: [
{
name: "Sally",
age: 7
},
{
name: "Ben",
age: 10
},
{
name: "Emily",
age: 2
}
],
numOfToys: 11
}
I have this already:
const returnedChild = await ChildModel.findById(BenId)
.populate('siblings', 'name age')
.select('name age siblings')
.lean()
How do I include the count of the toy in the returned object?
Just use virtual schemas:
ChildSchema.virtual('toyCount').get(function () {
return this.toys.length
});
const returnedChild = await ChildModel.findById(BenId)
.populate('siblings', 'name age')
.select('name age siblings')
.lean()
console.log("Toys -> ", returnedChild.toyCount)

Function that returns specified values, from an array of objects, if a property is true

Im working on a challenge: Given an array of objects, write a function admin that returns the name and birthdate of all users that are marked as "admin".
const users = [
{
name: 'Homer',
role: 'clerk',
dob: '12/02/1988',
admin: false
},
{
name: 'Lisa',
role: 'staff',
dob: '01/30/1965',
admin: false
},
{
name: 'Marge',
role: 'associate',
dob: '09/10/1980',
admin: true
}
]
function admin(myObj) {
function groupBy(myObj, prop) {
return myObj.reduce(function (acc, obj) {
let key = obj[prop]
if (!acc[key]) {
acc[key] = []
}
acc[key].push(obj)
return acc
}, {});
}
let trueAdmin = groupBy(myObj, 'admin');
console.log(trueAdmin[true].map(({name, dob}) => `Name: ${name}
Dob: ${dob}
`).join(`
`));
}
admin(users);
When I run tests, it comes back as undefined and i do not know why. Im looking for an expected output:
admin(users)
// Name: Marge
// Dob: 09/10/1980
If you just want to log them to the console you can simply use forEach()
const users = [
{
name: 'Homer',
role: 'clerk',
dob: '12/02/1988',
admin: false
},
{
name: 'Lisa',
role: 'staff',
dob: '01/30/1965',
admin: false
},
{
name: 'Marge',
role: 'associate',
dob: '09/10/1980',
admin: true
}
]
function admin(myArray) {
myArray.forEach(user => {
if (user.admin) console.log(`Name: ${user.name}, DOB: ${user.dob}`);
});
}
admin(users);
If you want to return the matched users you can use filter() to return an array filtered by a callback, and then map() that to alter the elements of the filtered array so they conform to the expected output.
In the following snippet admin() returns an array of mutated user objects with only name and dob keys.
const users = [
{
name: 'Homer',
role: 'clerk',
dob: '12/02/1988',
admin: false
},
{
name: 'Lisa',
role: 'staff',
dob: '01/30/1965',
admin: false
},
{
name: 'Marge',
role: 'associate',
dob: '09/10/1980',
admin: true
}
]
function admin(myArray) {
return myArray.filter(u => u.admin).map(u => ({name: u.name, dob: u.dob}));
}
console.log(admin(users));
filter(u => u.admin) iterates over each object and returns an array containing elements for which user.admin is true.
map(u => ({name: u.name, dob: u.dob})) then iterates over each element of the filtered array and changes each one to be an object containing just name: and dob: of the passed object.

Check if a merged array has item from two arrays and update the merged array JS

I have two arrays. I merge it and remove duplicates. Merging is done by taking into consideration all keys of the arrays (all key values in the object in array 1 must match with those in array 2).
var result1 = [{
name: 'Sandra',
email: 'sandra#example.com'
},
{
name: 'John',
email: 'johnny#example.com'
},
{
name: 'Peter',
email: 'peter#example.com'
},
{
name: 'Bobby',
email: 'bobby#example.com'
},
{
name: 'Arun',
email: 'arun#gmail.com'
},
];
var result2 = [{
name: 'John',
email: 'johnny#example.com'
},
{
name: 'Bobby',
email: 'bobby#example.com'
},
{
name: 'Arun',
email: 'arun#example.com'
}
];
var result= _.uniqWith(_.concat(result1, result2), _.isEqual)
Now I need to check each item of merged array with each item of array1 and array2 and update the merged array if they are present or not.
So my end result should be like this.
var result = [{
name: 'Sandra',
email: 'sandra#example.com',
presentInA: true,
presentInB: false
},
{
name: 'John',
email: 'johnny#example.com',
presentInA: true,
presentInB: true
},
{
name: 'Peter',
email: 'peter#example.com',
presentInA: true,
presentInB: false
},
{
name: 'Bobby',
email: 'bobby#example.com',
presentInA: true,
presentInB: true
},
{
name: 'Arun',
email: 'arun#example.com',
presentInA: false,
presentInB: true
},
{
name: 'Arun',
email: 'arun#gmail.com',
presentInA: true,
presentInB: false
}
];
How do I go about doing this in the best way? I think I can do it with iterating through all 3 arrays, but its a bad way of doing that.
Please advice.
You can do something like this
result.map(
per => ({
name: per.name,
email: per.email,
presentInA: _.find(
result1, (o) => o.nombre === per.nombre && o.email === per.email
) ? true : false,
presentInB: _.find(
result2, (o) => o.nombre === per.nombre && o.email === per.email
) ? true : false,
})
)
You could iterate over result and use _.some in order to check whether each object inside it is in result1 and result2 (and set the corresponding properties presentInA and presentInB). Something like this:
_.forEach(result, (obj) => {
let presentInA = _.some(result1, obj);
let presentInB = _.some(result2, obj);
obj.presentInA = presentInA;
obj.presentInB = presentInB;
});
var result1 = [{
name: 'Sandra',
email: 'sandra#example.com'
},
{
name: 'John',
email: 'johnny#example.com'
},
{
name: 'Peter',
email: 'peter#example.com'
},
{
name: 'Bobby',
email: 'bobby#example.com'
},
];
var result2 = [{
name: 'John',
email: 'johnny#example.com'
},
{
name: 'Bobby',
email: 'bobby#example.com'
},
{
name: 'Arun',
email: 'arun#example.com'
}
];
var result = _.uniqWith(_.concat(result1, result2), _.isEqual);
_.forEach(result, (obj) => {
let presentInA = _.some(result1, obj);
let presentInB = _.some(result2, obj);
obj.presentInA = presentInA;
obj.presentInB = presentInB;
});
console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.10/lodash.js"></script>
You could use a hashtable for finding & merging dupes:
// Merges objects with the same name and email, sets the [arrName] property of the merged object to true
// also takes an optional settings object for chaining
function merge(arr, arrName, { hash = {}, result = [] } = {}) {
for(const { name, email } of arr) {
// Build up a hopefully unique key
const key = name + "#" + email;
// If its not in the hash yet, add it to the hash and the result
if(!hash[key])
result.push(hash[key] = { name, email });
// then set the key
hash[key][arrName] = true;
}
// allow chaining by exposing both hash and result
return { hash, result, merge: (arr, arrName) => merge(arr, arrName, { hash, result }) };
}
That could be used as:
const { result } = merge(result1, "presentInA").merge(result2, "presentInB");
That is O(n) but assumes that emails do not contain two #s.

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

Categories