Merge objects in array without overriding different value properties - javascript

I'm trying to merge objects in an array with the same id without overriding different value properties.
var arr = [{
Messages: { count: 1 },
Account: { key: 'TEST' },
id: 179,
Contact:
{
firstName: 'The Postman',
lastName: 'Team'
},
Tags: { name: 'forums', color: '#0091EA' }
},
{
Messages: { count: 1 },
Account: { key: 'TEST' },
id: 179,
Contact:
{
firstName: 'The Postman',
lastName: 'Team'
},
Tags: { name: 'defective', color: '#0091EA' }
}];
var tags = [];
for(var i=0; i<arr.length; i++){
tags = tags.concat(arr[i].Tags);
}
var result = arr[0];
result.Tags = tags;
console.log(result);
My goal is is to have the following object:
var obj =
{ Messages: { count: 1 },
Account: { key: "TEST" },
id: 179,
Contact: { firstName: "The Postman", lastName: "Team" },
Tags: [{ name: "forums", color: "#0091EA" }, { name: "defective", color: "#0091EA" }]
};
I've created a fiddle where I managed to get the desired output, but I'm sure there is a better way to do it.
http://jsfiddle.net/18mLhx7j/1/
UPDATE
Based on the answer posted by #Harun Yilmaz I was able to accomplish the same result using Lodash reduce.
I just wonder if it's a valid alternative to what he posted.
var arr = [
{ Messages: { count: 1 },
Account: { key: "TEST" },
id: 179,
Contact: { firstName: "The Postman", lastName: "Team" },
Tags: { name: "forums", color: "#0091EA" } },
{ Messages: { count: 1 },
Account: { key: "TEST" },
id: 179,
Contact: { firstName: "The Postman", lastName: "Team" },
Tags: { name: "defective", color: "#0091EA" } }
];
var interactions =_.reduce(arr, function(acc, cur) {
for (let i =0; i < Object.keys(cur).length; i++) {
let key = Object.keys(cur)[i];
if (!acc[key]) {
acc[key] = cur[key];
} else if (acc[key] && !_.isArray(acc[key]) && !_.isEqual(acc[key], cur[key])) {
var obj = [];
obj.push(acc[key]);
obj.push(cur[key]);
acc[key] = obj;
} else if (acc[key] && _.isArray(acc[key])) {
acc[key].push(cur[key]);
}
}
return acc;
}, {});
console.log(interactions);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.15/lodash.min.js"></script>

You can use Array.reduce() to have a final object and spread operator as following
var arr = [
{ Messages: { count: 1 },
Account: { key: "TEST" },
id: 179,
Contact: { firstName: "The Postman", lastName: "Team" },
Tags: { name: "forums", color: "#0091EA" } },
{ Messages: { count: 1 },
Account: { key: "TEST" },
id: 179,
Contact: { firstName: "The Postman", lastName: "Team" },
Tags: { name: "defective", color: "#0091EA" } }
];
const finalArr = arr.reduce((acc, cur) => {
const {Tags,...Rest} = cur;
acc.Tags.push(Tags);
acc = {
...Rest,
Tags: acc.Tags
};
return acc;
},{Tags:[]});
// ^^ initial object
console.log(finalArr);

Related

Convert array into shorter array

I have array object -
[
{
department:"Reviewer",
name:"John"
},
{
department:"Reviewer",
name:"Carol"
},
{
department:"Reviewer",
name:"June"
},
{
department:"Assistant Reviewer",
name:"Mac"
},
{
department:"Assistant Reviewer",
name:"Issac"
}
]
I want to convert this object into array object as below -
[
{
department:"Reviewer",
name:"John;Carol;June;"
},
{
department:"Assistant Reviewer",
name:"Mac;Issac;"
},
]
I tried using filter -
[... new Set(obj.department)].map((item)=>{return {item.name+";"} });
But this operation is dealing with only one element per item. Its not accumulating names.
You can use reduce to achieve the desired result:
const array = [
{ department: "Reviewer", name: "John" },
{ department: "Reviewer", name: "Carol" },
{ department: "Reviewer", name: "June" },
{ department: "Assistant Reviewer", name: "Mac" },
{ department: "Assistant Reviewer", name: "Issac" }
];
const result = array.reduce((acc, cur) => {
const foundIndex = acc.findIndex(department => department.department === cur.department);
if (foundIndex === -1) {
acc.push({
department: cur.department,
name: cur.name + ';'
});
} else {
acc[foundIndex].name += cur.name + ';';
}
return acc;
}, []);
console.log(result);
Get a set of distinct departments, then map from them to the list of names
const data = [{"department":"Reviewer","name":"John"},{"department":"Reviewer","name":"Carol"},{"department":"Reviewer","name":"June"},{"department":"Assistant Reviewer","name":"Mac"},{"department":"Assistant Reviewer","name":"Issac"}]
console.log([...new Set(data.map(i=>i.department))].map(i=>({
department: i,
name: data.filter(j=>j.department===i).map(j=>j.name).join(';')+';'})))
or, using reduce:
const data = [{"department":"Reviewer","name":"John"},{"department":"Reviewer","name":"Carol"},{"department":"Reviewer","name":"June"},{"department":"Assistant Reviewer","name":"Mac"},{"department":"Assistant Reviewer","name":"Issac"}]
console.log(Object.values(data.reduce((a,{department, name})=>
(((a[department]??={department,name:''}).name+=`${name};`),a),{})))
This would also work:
const input = [ { department: "Reviewer", name: "John", }, { department: "Reviewer", name: "Carol", }, { department: "Reviewer", name: "June", }, { department: "Assistant Reviewer", name: "Mac", }, { department: "Assistant Reviewer", name: "Issac", }, ];
const output = input.reduce((prev, { department, name }) => {
const match = prev.find((item) => item.department === department);
if (match) {
match.name = `${match.name};${name}`;
} else {
prev.push({ department, name });
}
return prev;
}, []);
console.log(output);
Using Array.prototype.reduce() and Array.prototype.find()
Here is a solution using:
.reduce() to build an object that maps from department (aka key) to names (aka value)
.map() to transform that object into an array of objects with desired format
Note that this solution is faster than an array index search (aka other answers here) for large a large input array, at the cost of using a bit more memory.
const array = [
{ department: "Reviewer", name: "John" },
{ department: "Reviewer", name: "Carol" },
{ department: "Reviewer", name: "June" },
{ department: "Assistant Reviewer", name: "Mac" },
{ department: "Assistant Reviewer", name: "Issac" }
];
const result = Object.entries(array.reduce((acc, obj) => {
if(acc[obj.department]) {
acc[obj.department] += obj.name + ';';
} else {
acc[obj.department] = obj.name + ';';
}
return acc;
}, {})).map(arr => ({ department: arr[0], name: arr[1] }));
console.log(result);
This codes should work:
let data = [
{ department: "Reviewer", name: "John" },
{ department: "Reviewer", name: "Carol" },
{ department: "Reviewer", name: "June" },
{ department: "Assistant Reviewer", name: "Mac" },
{ department: "Assistant Reviewer", name: "Issac" }
];
let newData = [];
data.forEach((obj)=>{
if(newData.filter(m => m.department == obj.department).length) {
newData.find(n => n.department == obj.department).name += obj.name + ';';
} else {
newData.push({
department: obj.department,
name: obj.name + ';'
})
}
});
console.log(newData);

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

How to remove underscore from loop items?

I am trying to remove all the _er and _bx from the array, how can I do it? The way I tried doesn't seem to work. I'd like to see a solution where it removes all after _, and aswell only the letter that I put in for e.g remove all _ with er after.
const nullValue = {
collection: [{
name: "test_er"
},
{
name: "test_bx"
},
{
name: "fred"
},
{
name: "test_er"
}
]
};
const newArr = []
for (let [key, item] of nullValue.collection.entries()) {
item.name.replace(/_er/g, '')
newArr.push(item)
}
console.log(newArr)
Is this what you're looking for?
const nullValue = {
collection: [
{
name: 'test_er',
},
{
name: 'test_bx',
},
{
name: 'fred',
},
{
name: 'test_er',
},
],
};
nullValue.collection = [
...nullValue.collection.map(item => ({
name: item.name.replace(/_.*$/, ''),
})),
];
console.log(nullValue);
You can also use .split('_')[0] with the map method similar to Dmitry's answer... This gives you the first string of the split array, split at the underscore...
const nullValue = {
collection: [{
name: "test_er"
},
{
name: "test_bx"
},
{
name: "fred"
},
{
name: "test_er"
}
]
};
nullValue.collection = [ ...nullValue.collection.map( names => ({ name: names.name.split('_')[0], })),]
console.log(nullValue)
If you want to keep the original array of objects...
const nullValue = {
collection: [{
name: "test_er"
},
{
name: "test_bx"
},
{
name: "fred"
},
{
name: "test_er"
}
]
};
const newArr = { collection :
[ ...nullValue.collection.map( names =>
({ name: names.name.split('_')[0], })),
]}
console.log('newArr = ', newArr)
console.log('nullValue = ', nullValue)
You were VERY close with your original code, but the mistake was that String.replace() does not operate in-place, but rather returns its result. I've modified your code and added a comment below:
const nullValue = {
collection: [{
name: "test_er"
},
{
name: "test_bx"
},
{
name: "fred"
},
{
name: "test_er"
}
]
};
const newArr = []
for (let [key, item] of nullValue.collection.entries()) {
// My change is here
newArr.push( item.name.replace(/_er/g, '') )
}
console.log(newArr)
const nullValue = {
collection: [
{
name: "test_er"
},
{
name: "test_bx"
},
{
name: "fred"
},
{
name: "test_er"
}
]
};
nullValue.collection = nullValue.collection.map(i => i.name.replace(/_.*$/, ''))
console.log(nullValue)
This is preferable to .map() since you don't need a new array. You just want to change the strings:
const nullValue = {
collection: [
{ name: "test_er" },
{ name: "test_bx" },
{ name: "fred" },
{ name: "test_er" }
]
};
nullValue.collection.forEach(i => i.name = i.name.replace(/_.*$/, ''));
console.log(nullValue.collection);

How to merge and return new array from object in es6

Suppose there are two objects.
const a = [
{ id: '1-1-1', name: 'a111' },
{ id: '1-1-2', name: 'a112' },
{ id: '1-2-1', name: 'a121' },
{ id: '1-2-2', name: 'a122' },
{ id: '2-1-1', name: 'a211' },
{ id: '2-1-2', name: 'a212' }
]
const b = ['1-1', '1-2', '2-1']
and the result
{
'1-1':[
{ id: '1-1-1', name: 'a111' },
{ id: '1-1-2', name: 'a112' },
],
'1-2':[
{ id: '1-2-1', name: 'a121' },
{ id: '1-2-2', name: 'a122' },
],
'2-1':[
{ id: '2-1-1', name: 'a211' },
{ id: '2-1-2', name: 'a212' },
]
}
Basically, I want to group the data.
I use includes to check if the item from b to match the id from a. Then construct the new array.
This is my attempt(fiddle):
return b.map(item => a.map(jtem => {
if(jtem.id.includes(item)){
return {
[item]: jtem
}
}
}))
For somehow, it doesn't work.
and, is there a clever way to avoid the nested for loop or map function?
You can do that in following steps:
Apply reduce() on the array b
During each iteration use filter() on the the array a
Get all the items from a which starts with item of b using String.prototype.startsWith()
At last set it as property of the ac and return ac
const a = [
{ id: '1-1-1', name: 'a111' },
{ id: '1-1-2', name: 'a112' },
{ id: '1-2-1', name: 'a121' },
{ id: '1-2-2', name: 'a122' },
{ id: '2-1-1', name: 'a211' },
{ id: '2-1-2', name: 'a212' }
]
const b = ['1-1', '1-2', '2-1']
let res = b.reduce((ac,b) => {
ac[b] = a.filter(x => x.id.startsWith(b));
return ac;
},{})
console.log(res)
As suggested by #Falco is the comments that It would be better to scan over the a once as its large. So here is that version.Actually its better regarding performance
const a = [
{ id: '1-1-1', name: 'a111' },
{ id: '1-1-2', name: 'a112' },
{ id: '1-2-1', name: 'a121' },
{ id: '1-2-2', name: 'a122' },
{ id: '2-1-1', name: 'a211' },
{ id: '2-1-2', name: 'a212' }
]
const b = ['1-1', '1-2', '2-1']
let res = a.reduce((ac,x) => {
let temp = b.find(y => x.id.startsWith(y))
if(!ac[temp]) ac[temp] = [];
ac[temp].push(x);
return ac;
},{})
console.log(res)
Note: startsWith is not supported by I.E. So you can create polyfill using indexOf
if(!String.prototype.startWith){
String.prototype.startsWith = function(str){
return this.indexOf(str) === 0
}
}

Object.assign make duplicate when post a request

I create a ticket using zendesk but I didn't know why this happens
here is node js code:
config.js
baseTicketObject: {
'comment': {
'body': null,
},
'requester': {
'name': null,
'email': null,
},
'custom_fields': [],
},
create ticket api
function createTicketObjectFromRequest(req) {
const requestBody = req.body;
console.log('requestBody', requestBody);
console.log('config.baseTicketObject', config.baseTicketObject);
const ticket = Object.assign(config.baseTicketObject, {});
//console.log('ticket', ticket);
const {
messageBody, email, name, customFields,
} = requestBody;
//console.log('ticket.custom_fields', ticket.custom_fields);
// Request must contain a name, email and body
ticket.requester.name = name;
ticket.requester.email = email;
ticket.comment.body = messageBody;
if (req.user && req.user.id) {
ticket.custom_fields.push(createCustomFieldObject(config.customFieldNameToZendeskFieldIdMapping['userId'], Number(req.user.id)));
}
Object.keys(config.customFieldNameToZendeskFieldIdMapping).forEach((fieldName) => {
if (config.customFieldNameToZendeskFieldIdMapping[fieldName] === config.customFieldNameToZendeskFieldIdMapping.userId) {
return;
}
//console.log('fieldName', fieldName);
const mappedCustomFieldId = config.customFieldNameToZendeskFieldIdMapping[fieldName];
if (mappedCustomFieldId) {
ticket.custom_fields.push(createCustomFieldObject(mappedCustomFieldId, !customFields[fieldName] ? '' : customFields[fieldName]));
}
});
return { ticket: ticket };
}
whenever I post a request the config.baseTicketObject will keep all items i pushed before like this
config.baseTicketObject { comment: { body: null },
requester: { name: null, email: null },
custom_fields: [] }
-------------------------------------
config.baseTicketObject { comment: { body: 'dgfhdgfhdgfh dgfhdfghdfg' },
requester: { name: 'test other', email: 'tranthiphuonghue96#yopmail.com' },
custom_fields:
[ { id: 360010481051, value: '' },
{ id: 360010510411, value: '' },
{ id: 360010406792, value: '' },
{ id: 360010511011, value: '' },
{ id: 360010511191, value: '' },
{ id: 360010920852, value: 'contact_support' } ] }
---------------------------------------------------------
config.baseTicketObject { comment: { body: 'dgfhdgfhdgfh dgfhdfghdfg' },
requester: { name: 'test other', email: 'tranthiphuonghue96#yopmail.com' },
custom_fields:
[ { id: 360010481051, value: '' },
{ id: 360010510411, value: '' },
{ id: 360010406792, value: '' },
{ id: 360010511011, value: '' },
{ id: 360010511191, value: '' },
{ id: 360010920852, value: 'contact_support' },
{ id: 360010481051, value: '' },
{ id: 360010510411, value: '' },
{ id: 360010406792, value: '' },
{ id: 360010511011, value: '' },
{ id: 360010511191, value: '' },
{ id: 360010920852, value: 'contact_support' } ] }
I don't know why the config.baseTicketObject like that, please help.
Reverse parameters order in Object.assing.
You have
Object.assign(config.baseTicketObject, {});
but should be
Object.assign({}, config.baseTicketObject);
Object.assign syntax
Object.assign(target, ...sources)
In your case
const ticket = Object.assign({}, config.baseTicketObject);
Edit:
Add
ticket.custom_fields = [];
after
const ticket = Object.assign({}, config.baseTicketObject);
because Object.assign create shallow copy, witch mean that ticket.custom_fields still holds reference to original array object from config.baseTicketObject.custom_fields

Categories