Create new object from two other by matching values in JavaScript - javascript

The point is to create new object from existing listOfStudents and homeworkResults objects that are matched by email value. The goal is to use only Javascript.
An example of new object that starts like:
{
name: 'John',
email: 'john#gmail.com',
results: [
{
topic: 'HTML Basics',
success: true
},
{
topic: 'CSS Basics',
success: false
}
]
},
{
name: 'Jane',
email: 'jane#gmail.com',
results: [
{
topic: 'HTML Basics',
success: true
},
{
topic: 'CSS Basics',
success: true
}
]
}
here is an example of my code. something is wrong with addHwResults funktion because it is not excludes hwResults for other student.
var listOfStudents = [
{ name: 'John', email: 'john#gmail.com' },
{ name: 'Jane', email: 'jane#gmail.com' }
];
var hwResults = [
{ topic: 'HTML Basics', results: [ { email: 'john#gmail.com', success: true }, { email: 'jane#gmail.com', success: true } ] },
{ topic: 'CSS Basics', results: [ { email: 'john#gmail.com', success: false }, { email: 'jane#gmail.com', success: true } ] }
];
function Stud(name, email) {
const hwResults = [];
this.addHwResult = function(topic, success) {
const result = {
topic: topic,
success: success
};
hwResults.push(result);
}
this.getHomeworkResult = function() {
return hwResults;
}
}
function Lab(studentsList){
const stud = new Stud();
this.printStudList = function() {
studentsList.forEach(el => {
console.log(stud.getHomeworkResult());
});
}
this.addHwResults = function(objGet) {
objGet.results.forEach(el => {
const resultByEmail = studentsList.find(elem => elem.email === el.email);
stud.addHwResult(objGet.topic, el.success);
});
}
}
const lab = new Lab(listOfStudents);
lab.addHwResults(hwResults[0]);
lab.addHwResults(hwResults[1]);
lab.printStudList();
wrong result looks like:
enter image description here

Maintains an object map for { [email]=>Stud objects }.
Iterate on original studentList object and use [email] as key for Stud object lookup:
var listOfStudents = [
{ name: 'John', email: 'john#gmail.com' },
{ name: 'Jane', email: 'jane#gmail.com' }
];
var hwResults = [
{ topic: 'HTML Basics', results: [ { email: 'john#gmail.com', success: true }, { email: 'jane#gmail.com', success: true } ] },
{ topic: 'CSS Basics', results: [ { email: 'john#gmail.com', success: false }, { email: 'jane#gmail.com', success: true } ] }
];
function Stud(name, email) {
const hwResults = [];
this.addHwResult = function(topic, success) {
const result = {
topic: topic,
success: success
};
hwResults.push(result);
}
this.getHomeworkResult = function() {
return hwResults;
}
}
function Lab(studentsList){
const studs = Object.fromEntries(studentsList.map(({name,email})=>[email,new Stud(name,email)]));
this.printStudList = function() {
studentsList.forEach(({name, email}) => {
console.log({ name, email, results: studs[email].getHomeworkResult()});
});
}
this.addHwResults = function(objGet) {
objGet.results.forEach(({email,success}) => {
studs[email].addHwResult(objGet.topic, success);
});
}
}
const lab = new Lab(listOfStudents);
lab.addHwResults(hwResults[0]);
lab.addHwResults(hwResults[1]);
lab.printStudList();
Creates student object map, iterates over all results and populates student results list. Unwraps object map into list.
var listOfStudents = [
{ name: 'John', email: 'john#gmail.com' },
{ name: 'Jane', email: 'jane#gmail.com' },
{ name: 'failure', email: 'wat#aol.com' },
{ name: 'tryhard', email: 'imcool#hotmail.com' }
];
var hwResults = [
{ topic: 'HTML Basics', results: [ { email: 'john#gmail.com', success: true }, { email: 'jane#gmail.com', success: true } ] },
{ topic: 'CSS Basics', results: [ { email: 'john#gmail.com', success: false }, { email: 'jane#gmail.com', success: true }, { email: 'imcool#hotmail.com', success: false } ] }
];
const stuMap = Object.fromEntries(
listOfStudents.map(x=>[x.email,{...x, results: []}]) )
hwResults.forEach( ({topic,results})=>
results.forEach( ({email,success})=>
stuMap[email].results.push({topic,success}) ))
console.log(
Object.values(stuMap)
)

Related

JavaScript merge (AJAX) array objects with the same keys

My application is MVC 5, I am using the following Ajax to generate an array:
$.ajax({
type: "Post",
url: '#Url.Action("Getthereport", "Surveys")',
async: false,
cache: false,
dataType: "json",
data: { 'test': "All" },
success: function (result) {
if (result && result.Grid.length > 0) {
for (let i = 0; i < result.Grid.length; i++) {
jsonData.push({
Question: result.Grid[i].Body,
QuestionId: result.Grid[i].QuestionId,
myData: { name: result.Grid[i].name, value: result.Grid[i].value }
});
};
}
},
complete: function () {
reduce();
},
error: function(err) {
alert(err.status + " : " + err.statusText);
}
});
I generates the following:
var jsonData = [
{
Question: "Was the training useful?",
QuestionId: 1,
myData: [{ name: 'No', value: 1 }] },
{
Question: "Was the training useful?",
QuestionId: 1 ,
myData: [{ name: 'Yes', value: 1 }]
}];
to merge the objects, I use:
const result = Object.values(jsonData.reduce((acc, obj) => {
if (!acc[obj.QuestionId]) {
acc[obj.QuestionId] = obj;
} else {
acc[obj.QuestionId].myData = acc[obj.QuestionId].myData.concat(obj.myData);
}
return acc;
Works great if the array is hardcoded and generates:
var jsonData = [
{
Question: "Was the training useful?",
QuestionId: 1,
myData: [{ name: 'No', value: 1 },
{ name: 'Yes', value: 1 }]
}];
However, if the array is generated by Ajax call, I get the following error:
acc[obj.QuestionId].myData.concat is not a function
I tried to run the reduce script on Ajax complete and directly both did not work.
In the success property of your ajax call options you're pushing myData as an object, not as an array
success: function (result) {
if (result && result.Grid.length > 0) {
for (let i = 0; i < result.Grid.length; i++) {
jsonData.push({
Question: result.Grid[i].Body,
QuestionId: result.Grid[i].QuestionId,
myData: { name: result.Grid[i].name, value: result.Grid[i].value }
});
};
}
},
Which means that the output is not as you stated but rather
var jsonData = [
{
Question: "Was the training useful?",
QuestionId: 1,
myData: { name: 'No', value: 1 } //<-- Objects not arrays
},
{
Question: "Was the training useful?",
QuestionId: 1,
myData: { name: 'Yes', value: 1 }
}
];
You can either declare it as an array at that stage to generate the output you originally posted,
for (let i = 0; i < result.Grid.length; i++) {
jsonData.push({
...
myData: [{ name: result.Grid[i].name, value: result.Grid[i].value }]
});
};
or adjust your reduce.
var jsonData = [
{
Question: "Was the training useful?",
QuestionId: 1,
myData: { name: 'No', value: 1 }
},
{
Question: "Was the training useful?",
QuestionId: 1,
myData: { name: 'Yes', value: 1 }
}
];
const result = Object.values(jsonData.reduce((acc, obj) => {
acc[obj.QuestionId] ??= { ...obj, myData: [] };
acc[obj.QuestionId].myData.push(obj.myData);
return acc;
}, {}));
console.log(JSON.stringify(result, null, 2))

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

Need to return existing data structure with update objects

I wanted to update the object when it pass the check but getting different result then expected.
What is preferred way to get the expected?
This the sample data:
var abc = [
{
type: "manager",
members: [{name: 'bob'}, {name: 'rob'}]
},
{
type: "clerk",
members: [{name: 'foo'}, {name: 'bar'}]
}
];
Using this function :
function funn() {
return abc.map((cate) => {
return cate.members.map((mem) => {
if (mem.name === 'bob') {
mem['isBob'] = true;
}
return mem;
});
});
}
I wanted in this format (expected):
[
{
type: 'manager',
members: [{ name: 'bob', isBob: true }, { name: 'rob' }],
},
{
type: 'clerk',
members: [{ name: 'foo' }, { name: 'bar' }],
},
];
The actual is like this:
[
{
members: [{ name: 'bob' }, { name: 'rob' }],
},
{
members: [{ name: 'foo' }, { name: 'bar' }],
},
];
You just need to find the element in the members array and if it is present then add the isBob property.
var abc = [{
type: "manager",
members: [{
name: "bob"
}, {
name: "rob"
}],
},
{
type: "clerk",
members: [{
name: "foo"
}, {
name: "bar"
}],
},
];
const result = abc.map((obj) => {
const isExist = obj.members.find((o) => o.name === "bob");
if (isExist) isExist.isBob = true;
return obj;
});
console.log(result);
The output you showed for your code is not matching when i run it.
Here I have modified your code a bit:
Instead of returning an array from the outer .map(), I am returning an object which has a type and members category.
function funn() {
return abc.map((cate) => {
let cateMembers = cate.members.map((mem) => {
if (mem.name === 'bob') {
mem['isBob'] = true;
}
return mem;
});
return { type : cate.type , members : cateMembers};
});
}
Remember to check like this : console.log(funn())
You could do something like this:
var abc = [
{
type: "manager",
members: [{name: 'bob'}, {name: 'rob'}]
},
{
type: "clerk",
members: [{name: 'foo'}, {name: 'bar'}]
}
];
function funn() {
abc.forEach(el => {
el.members.forEach(el2 => {
if(el2.name === 'bob') el2.isBob = true
})
})
return abc
}
console.log(funn())

How to remove TextRow and add a string to JSON in NodeJs

I want to remove TextRow and add a string(true) to JSON in NodeJs. I have added below my code.
NodeJs Code:
function groupBy(objectArray, property) {
return objectArray.reduce(function (acc, obj) {
let key = obj[property]
if (!acc[key]) {
acc[key] = []
}
acc[key].push(obj)
return acc
}, {})
}
group data :
[
TextRow { name: '/products', email: '111#gmail.com' },
TextRow { name: '/products', email: '222#gmail.com' },
TextRow { name: '/sales', email: '111#gmail.com' },
TextRow { name: '/sales', email: '222#gmail.com' },
TextRow { name: '/sales', email: '333#gmail.com' },
TextRow { name: '/sales', email: '444#gmail.com' },
TextRow { name: '/finance', email: '333#gmail.com' },
TextRow { name: '/finance', email: '444#gmail.com' },
]
My output:
{
'/products': [
TextRow { name: '/products', email: '111#gmail.com' },
TextRow { name: '/products', email: '222#gmail.com' },
],
'/sales': [
TextRow { name: '/products', email: '111#gmail.com' },
TextRow { name: '/products', email: '222#gmail.com' },
TextRow { name: '/products', email: '333#gmail.com' },
TextRow { name: '/products', email: '444#gmail.com' },
],
'/products': [
TextRow { name: '/products', email: '333#gmail.com' },
TextRow { name: '/products', email: '444#gmail.com' },
],
}
Output Should be:
{
'/products': [
{
'111#gmail.com': true,
'222#gmail.com': true,
}
],
'/sales': [
{
'111#gmail.com': true,
'222#gmail.com': true,
'333#gmail.com': true,
'444#gmail.com': true,
}
],
'/finance': [
{
'333#gmail.com': true,
'444#gmail.com': true,
}
]
}
Instead of pushing the entire row, you want to create a new object. I'm not quite sure why your final output is an array with a single object though or why there is a true for each email.
const key = obj[property];
if (!acc[key]) {
acc[key] = [{}];
}
acc[key][0][obj.email] = true;
return acc;
Doing something like this will result in an object whose keys are the name and the values are each an array with a single object whose keys are the email addresses.
Here is an example on how you should do it. It will give you the expected result
const list = [{
TextRow: {
name: '/products',
email: '111#gmail.com'
}
},
{
TextRow: {
name: '/products',
email: '222#gmail.com'
}
}, {
TextRow: {
name: '/sales',
email: '111#gmail.com'
}
}, {
TextRow: {
name: '/sales',
email: '222#gmail.com'
}
}, {
TextRow: {
name: '/sales',
email: '333#gmail.com'
}
}, {
TextRow: {
name: '/sales',
email: '444#gmail.com'
}
}, {
TextRow: {
name: '/finance',
email: '333#gmail.com'
}
}, {
TextRow: {
name: '/finance',
email: '444#gmail.com'
}
},
]
const result = list.reduce((acc, x) => {
const name = x['TextRow']['name'];
const obj = {
[x['TextRow'].email]: true
};
if (acc[name]) {
acc[name].push(obj)
} else {
acc[name] = [obj];
}
return acc;
}, [])
console.log(result)

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