There are two object array, some of them have the same key, I'd like to merge the same key in the first array. I have pasted my code.I used nested loop, but the performance was bad O(n²). Maybe I need another method to enhance performance.(I can't use ES6 for some reason, so I'll appreciate if it is the ES5 method.)
var people = [
{
id: "001",
name: "David",
age: 29
},
{
id: "002",
name: "Lucia",
age: 41
},
{
id: "003",
name: "Steve",
age: 18
}
];
var address = [
{
id: "001",
city: "Barcelona"
},
{
id: "002",
city: "Paris"
},
{
},
{
id: "003",
city: "Tokyo"
},
{
id: "004",
city: "Barcelona"
}
];
My code
people.forEach(function(item) {
var id = item.id;
address.forEach(function(location) {
if (location.id == id) {
item.address = location.address
}
});
});
Result
var people = [
{
id: "001",
name: "David",
age: 29,
city: "Barcelona"
},
{
id: "002",
name: "Lucia",
age: 41,
city: "Paris"
},
{
id: "003",
name: "Steve",
age: 18,
city: "Tokyo"
}
];
The new people array is I preferred.
You could take a Map with all addresses and then map new object with extended properties of the map.
This approach takes all properties of address objects.
var people = [{ id: "001", name: "David", age: 29 }, { id: "002", name: "Lucia", age: 41 }, { id: "003", name: "Steve", age: 18 }],
address = [{ id: "001", city: "Barcelona" }, { id: "002", city: "Paris" }, {}, { id: "003", city: "Tokyo" }, { id: "004", city: "Barcelona" }],
map = new Map(address.map(o => [o.id, o])),
result = people.map(o => Object.assign({}, o, map.get(o.id)));
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Make a Map with cities by id, and use it when iterating over the people array to find out the city:
let cities = new Map(address.map(a => [a.id, a.city]));
let people2 = people.map(p => ( {...p, city: cities.get(p.id)} ));
You could use Array#map to iterate over people, and Array#find to find the corresponding address from id within iterations:
const people = [{id: "001",name: "David",age: 29 },{ id: "002", name: "Lucia", age: 41
},{ id: "003", name: "Steve", age: 18 }],
address = [{ id: "001", city: "Barcelona" },{ id: "002", city: "Paris" },{ },{ id: "003", city: "Tokyo" },{ id: "004", city: "Barcelona" }];
console.log(
people.map(p => ({
...p,
...address.find(a => (p.id === a.id))
}))
);
However, that's supposing that the properties' name of address's items are not the same as people's ones.
The code below is not tested but it should work
// create an object to store them
const mergedItems = {};
// merge the 2 arrays so you only map them once (just for shorter code)
people.concat(address).map(entity => {
// add each entity on the object and id as a key
mergedItems[entity.id] = {
// if the key exist, it will merge it with the new entity
...mergedItems[entity.id],
...entity,
}
)
// this is your merged items
// Object.values will convert it from object to array
const finalItems = Object.values(mergedItems);
I used map instead of for loop because it is faster: https://codeburst.io/javascript-map-vs-foreach-f38111822c0f
I have used Object.assign method to add values from address
var people = [{ id: "001", name: "David", age: 29 }, { id: "002", name: "Lucia", age: 41 }, { id: "003", name: "Steve", age: 18 }],
address = [{ id: "001", city: "Barcelona" }, { id: "002", city: "Paris" }, {}, { id: "003", city: "Tokyo" }, { id: "004", city: "Barcelona" }];
people.forEach(function(item,pos){
Object.assign(item,{},address[address.findIndex(o=>o.id == item.id)]);
});
console.log(people);
Related
I am writing code in ReactJs
I have an Array of object like this
[
{
createdBy: "DEF",
createdDate: "2020",
lastModifiedDate: "2021",
name: "John Doe",
section: {
createdBy: "A2C",
status: "ACTIVE",
},
sectionName: "Mechanical",
},
{
createdBy: "ABC",
createdDate: "2020",
lastModifiedDate: "2021",
name: "John Doe",
section: {
createdBy: "ABC",
status: "ACTIVE",
},
sectionName: "Mechanical",
},
{
createdBy: "ABC",
createdDate: "2020",
lastModifiedDate: "2021",
name: "John Doe",
section: {
createdBy: "XYZ",
status: "ACTIVE",
},
sectionName: "Mechanical",
},
{
createdBy: "A1C",
createdDate: "2020",
lastModifiedDate: "2021",
name: "John Wick",
section: {
createdBy: "ABC",
status: "ACTIVE",
},
sectionName: "Mechanical",
},
];
here, the only thing same is the "name", so on the basis of name, I want the duplicate objects to be stored in the new array.
I have tried it like this
let temp = [];
for (const i of response) {
if (!temp.includes(i)) {
temp.push(i);
console.log("if loop", i);
} else {
console.log("else loop", response);
}
}
but the control never goes to else as it considers each object as different.
I need the first occurrence of an object as it is, but when the "name" element gets repeated, that object should get stored in a new array.
Expected:-
[
{
createdBy: "ABC",
createdDate: "2020",
lastModifiedDate: "2021",
name: "John Doe",
section: {
createdBy: "ABC",
status: "ACTIVE",
},
sectionName: "Mechanical",
},
{
createdBy: "ABC",
createdDate: "2020",
lastModifiedDate: "2021",
name: "John Doe",
section: {
createdBy: "XYZ",
status: "ACTIVE",
},
sectionName: "Mechanical",
},
]
You can maintain a tracker object for checking if an object with the same name has already been visited or not. If visited then push the object into a duplicate array otherwise push into unique array. Try this-
const data=[{createdBy:"ABC",createdDate:"2020",lastModifiedDate:"2021",name:"John Doe",section:{createdBy:"ABC",status:"ACTIVE"},sectionName:"Mechanical"},{createdBy:"ABC",createdDate:"2020",lastModifiedDate:"2021",name:"John Doe",section:{createdBy:"A2C",status:"ACTIVE"},sectionName:"Mechanical"},{createdBy:"A1C",createdDate:"2020",lastModifiedDate:"2021",name:"John Doe",section:{createdBy:"ABC",status:"ACTIVE"},sectionName:"Mechanical"}];
const track = {};
const unique = [];
const duplicate = [];
for (const item of data) {
if (track?.[item.name] === undefined) {
unique.push(item);
track[item.name] = true;
} else {
duplicate.push(item);
}
}
console.log('unique', unique);
console.log('duplicate', duplicate);
.as-console-wrapper {min-height: 100%!important; top: 0}
let count=0
let temp = response;
let duplicateArray=[]
for (const i of response) {
count=0
if(temp.find(t => t.name === i.name)){
count++
}
if(count>0){
duplicateArray.push(i)
}
}
Not sure if this will solve the particular issue you have but
not use helprjs
const arr1 = [{ id: 1, name: 'Jack'}, { id: 2, name: 'Jack'}];
const arr2 = [{ id: 2, name: 'Jane'}, { id: 3, name: 'Rod'}];
mergeArrays(arr1, arr2, "name");
// [{ id: 1, name: 'Jack'}, { id: 2, name: 'Jane'}, { id: 3, name: 'Rod'}];
mergeArrays(arr1, arr2, "id");
// [{ id: 1, name: 'Jack'}, { id: 2, name: 'Jack'}, { id: 3, name: 'Rod'}];
Check out the demo
Been scratching my head on this one for an entire evening with no solution in sight.
Put simply
I am querying two arrays from two separate APIs.
They return data in following format:
API 1
[{
balance: 4444,
age: "18",
gender: "Male",
level: "2",
name: "Joe"
}, {
balance: 3333,
age: "45",
gender: "Male",
level: "3",
name: "Angel"
}
}]
API 2
{
Joe: {
score: 32
},
Angel: {
score: 22
}
}
I need to match the object keys from the second API to the name value of playerInfo from first API so a new array is made that is completely flat like this:
[{
balance: 4444,
age: "18",
gender: "Male",
level: "2",
name: "Joe",
score: 32
}, {
balance: 3333,
age: "45",
gender: "Male",
level: "3",
name: "Angel",
score: 22
}
}]
Here's where I am being stone walled at the moment
var result = []
const matchKeys = (data, data1) => {
let arr = []
arr.push(data1)
data.map(item => {
arr.map(item1 => {
if (item.name === Object.keys(item1)) {
result.push(Object.assign(item, item1))
console.log(result)
}
})
})
}
matchKeys(api1, api2)
I suspect I'm not getting very far because I am not properly accessing my second dataset because there is no index that keeps track of which object I am supposed to pair up with corresponding value in the arrays.
Appreciate any help
You can implement that using Array.map.
const input1 = [{
balance: 4444,
age: "18",
gender: "Male",
level: "2",
name: "Joe"
}, {
balance: 3333,
age: "45",
gender: "Male",
level: "3",
name: "Angel"
}];
const input2 = {
Joe: {
score: 32
},
Angel: {
score: 22
}
}
function matchKeys(arr1, arr2) {
const result = arr1.map((item) => {
if (input2[item.name]) {
return { ...item, ...input2[item.name] };
}
return item;
});
return result;
}
console.log(matchKeys(input1, input2));
you could use the property of the second object as a way to search the right name.
const input1 = [{
balance: 4444,
age: "18",
gender: "Male",
level: "2",
name: "Joe"
}, {
balance: 3333,
age: "45",
gender: "Male",
level: "3",
name: "Angel"
}];
const input2 = {
Joe: {
score: 32
},
Angel: {
score: 22
}
}
const matchKeys = (data, data1) => {
return data.map((item) => ({ ...item, score: data1[item.name] ? data1[item.name].score : 0 }));
}
console.log(matchKeys(input1, input2));
also checked if it has a name and if for some reason it didn't I inserted a default score.
I have a project data that has list of projects and associated employees. Each employee object has salary property. What I'm trying to do is to find the employee with maximum salary.
Example of code:
var projects = [
//Object(0)
{
projectName: "Winter",
projectCode: "O0123",
employee: [{
title: "Mr.",
name: "Tom",
id: 1005,
salary: 12345
},
{
title: "Mr.",
name: "Bunny",
id: 1009,
salary: 54321
},
{
title: "Mr.",
name: "Harris",
id: 1010,
salary: 23456
},
]
},
//Object(1)
{
projectName: "Summer",
projectCode: "P10406",
employee: [{
title: "Mr.",
name: "Seth",
id: 1006,
salary: 1234
},
{
title: "Mr.",
name: "Sam",
id: 1011,
salary: 654321
},
],
}
]
console.log(projects.length);
let maxSalary = 0;
for (var i = 0; i < projects.length; i++) {
console.log(projects[i].projectName);
for (var j = 0; j < projects[i].employee.length; j++) {
console.log("\t" + projects[i].employee[j].title + projects[i].employee[j].name + "\n" + "\t" + "Salary: " + projects[i].employee[j].salary);
if (i == 0 && j == 0) {
maxSalary <= projects[i].employee[j].salary;
}
if (projects[i].employee[j].salary > maxSalary) {
maxSalary = projects[i].employee[j].salary;
}
}
}
console.log("Max Salary = " + maxSalary);
Please suggest any inputs.
Simply loop through the different projects and then loop the employees to get the highest value.
var projects = [{
projectName: "Winter",
projectCode: "O0123",
employee: [
{title: "Mr.", name: "Tom", id: 1005, salary: 12345},
{title: "Mr.", name: "Bunny", id: 1009, salary: 54321},
{title: "Mr.", name: "Harris", id: 1010, salary: 23456}
]
},
{
projectName: "Summer",
projectCode: "P10406",
employee: [
{title: "Mr.", name: "Seth", id: 1006, salary: 1234},
{title: "Mr.", name: "Sam", id: 1011, salary: 654321}
]
}
];
var max = 0;
projects.forEach(p => p.employee.forEach(e => e.salary > max && (max = e.salary)));
console.log(max);
If you want to receive the employee, as you mentioned in your question, and not the salary, you could do it basically the same, just returning the whole object:
var projects = [{
projectName: "Winter",
projectCode: "O0123",
employee: [
{title: "Mr.", name: "Tom", id: 1005, salary: 12345},
{title: "Mr.", name: "Bunny", id: 1009, salary: 54321},
{title: "Mr.", name: "Harris", id: 1010, salary: 23456}
]
},
{
projectName: "Summer",
projectCode: "P10406",
employee: [
{title: "Mr.", name: "Seth", id: 1006, salary: 1234},
{title: "Mr.", name: "Sam", id: 1011, salary: 654321}
]
}
];
var max = {salary: 0};
projects.forEach(p => p.employee.forEach(e => e.salary > max.salary && (max = e)));
console.log(max);
I think these examples will give you a basic idea how to do this.
here you have to mix flatMap which will transform your array of array to simple flat array.
then you can use array reduce to pick up the highest salary.
const projects = [
{
employee: [{
salary: 12345
},
{
salary: 54321
},
{
salary: 23456
},
]
},
{
employee: [{
salary: 1234
},
{
salary: 654321
},
]
}
];
const salaries = projects.flatMap(project => {
// here we have array of array, goal is to craft flat array of salary.
return project.employee.map(employe => {
// From each employee, we pickup only the salary.
return employe.salary;
});
});
const highest = salaries.reduce((accumulator, currentValue) => {
// If current salary is highest than the previous, we keep track of it.
if(currentValue > accumulator) {
accumulator = currentValue;
}
return accumulator;
});
You already have the maximum salary, what you need to do is save the index of the employee with the highest salary as soon as you get it. You code should look like this.
var projects = [
//Object(0)
{
projectName: "Winter",
projectCode: "O0123",
employee: [{
title: "Mr.",
name: "Tom",
id: 1005,
salary: 12345
},
{
title: "Mr.",
name: "Bunny",
id: 1009,
salary: 54321
},
{
title: "Mr.",
name: "Harris",
id: 1010,
salary: 23456
},
]
},
//Object(1)
{
projectName: "Summer",
projectCode: "P10406",
employee: [{
title: "Mr.",
name: "Seth",
id: 1006,
salary: 1234
},
{
title: "Mr.",
name: "Sam",
id: 1011,
salary: 654321
},
],
}
]
console.log(projects.length);
let maxSalary = 0;
let employeeWithMaxSalary = {};
for (var i = 0; i < projects.length; i++) {
console.log(projects[i].projectName);
for (var j = 0; j < projects[i].employee.length; j++) {
console.log("\t" + projects[i].employee[j].title + projects[i].employee[j].name + "\n" + "\t" + "Salary: " + projects[i].employee[j].salary);
if (i == 0 && j == 0) {
maxSalary <= projects[i].employee[j].salary;
}
if (projects[i].employee[j].salary > maxSalary) {
maxSalary = projects[i].employee[j].salary;
employeeWithMaxSalary = projects[i].employee[j];
}
}
}
console.log("Max Salary = " + maxSalary);
console.log(employeeWithMaxSalary);
You can improve also improve your code by using ES6 syntax (arrow function and foreach) instead of having nested loops.
Initially, I'm having the object:
let root = {};
root["myRootNode"] = {
id1: {
age: "17",
name: "name1",
surname: "surname1"
},
id2: {
age: "11",
name: "name2",
surname: "surname2"
},
id3: {
age: "25",
name: "name1",
surname: "surname3"
}
};
And what I want to do with it, is to get it into the state:
"name1": [
{
age: "17",
surname: "surname1"
},
{
age: "25",
surname: "surname3"
}
],
"name2": [
age: "11",
surname: "surname2"
]
For me it's important to have the list/array of all objects, which contains this same property, grouped by the value of that property.
What I tried (using lodash) is:
let test = _.mapKeys(root["myRootNode"], function(value, key) {
return value["name"];
});
But this gives me the result:
"name1": {
age: "25"
name: "name1"
surname: "surname3"
},
"name2": {
age: "11"
name: "name2"
surname: "surname2"
}
So they are not grouped and only the last value is mapped under the key which is repeating. Also in the result that I got, they are not placed under an array.
Can use groupBy and map it's values to get rid of the name property.
If you don't mind leaving the name property can simply do _.groupBy(root.myRootNode, 'name');
Personally it feels like you should be using arrays instead of objects
const res =
_(root.myRootNode)
.groupBy('name')
.mapValues((arr)=>_.map(arr, (o) =>_.omit(o,['name'])))
.value()
console.log(res)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.10/lodash.min.js"></script>
<script>
let root = {};
root["myRootNode"] = {
id1: {
age: "17",
name: "name1",
surname: "surname1"
},
id2: {
age: "11",
name: "name2",
surname: "surname2"
},
id3: {
age: "25",
name: "name1",
surname: "surname3"
}
};
</script>
Here's a shot at it. The idea is to create an array for each new name and push all entries onto the corresponding name array.
const root = {
myRootNode: {
id1: {
age: "17",
name: "name1",
surname: "surname1"
},
id2: {
age: "11",
name: "name2",
surname: "surname2"
},
id3: {
age: "25",
name: "name1",
surname: "surname3"
}
}
};
const result = Object.values(root.myRootNode).reduce((a, e) => {
if (!(e.name in a)) {
a[e.name] = [];
}
a[e.name].push({
age: e.age,
surname: e.surname
});
return a;
}, {});
console.log(JSON.stringify(result, null, 4));
I have the data below:
[
{ id: 0, Department: "Civil", Value: "40000", Title:"Sustainability", ComID: "45", organisation: { City: "New York", ComID: 45, Country: "USA" } },
{ id: 1, Department: "Energy", Value: "82000", Title: "Wind Energy", ComID: "62", organisation: { City: "Paris" , ComID: 62, Country: "France" } },
{ id: 2, Department: "Medical", Value: "67000", Title: "Neuroscience", ComID: "21", organisation: { City: "Berlin", ComID: 21, Country: "Germany" } },
{ id: 3, Department: "Computer", Value: "100000", Title: "Security", ComID: "67", organisation: { City: "Amsterdam", ComID: 67, Country: "Holland" } }
]
the data is an array of about 100 objects like the ones above.
In the data I have organizations of the same country. I want to sum the Value attribute of each Country and put it in a new table.
For example I would like to create:
[ { Name: "each Country", Value: "the summed value" } ]
Edited to respond to updated question
You can use d3.nest() to group objects by a given key (in this case, the country) and then "roll them up" to sum the value across each of the items belonging to a given country. In your case, a one-liner would look something like this:
d3.nest().key(function(d){
return d.organisation.Country; })
.rollup(function(leaves){
return d3.sum(leaves, function(d){
return d.Value;
});
}).entries(data)
.map(function(d){
return { Country: d.key, Value: d.values};
});
Here I'm using d3.sum, passing in an accessor function to specify that you want to sum the Value of each item:
This returns four objects on your example data:
[{"Country":"USA","Value":40000},
{"Country":"France","Value":82000},
{"Country":"Germany","Value":67000},
{"Country":"Holland","Value":100000}]
Javascript converts the strings into numbers for you. Note that you have some typos in your example data that I had to fix, giving the following:
var data = [ { id:0,Department: "Civil",Value : "40000",Title :"Sustainability",ComID : "45", organisation:{ City:"New York",ComID:"45",Country: "USA"}}, { id:1,Department: "Energy",Value : "82000",Title : "Wind Energy",ComID : "62", organisation:{ City:"Paris",ComID:"62",Country: "France"}}, { id:2,Department: "Medical",Value : "67000",Title : "Neuroscience",ComID : "21", organisation:{ City:"Berlin",ComID:"21",Country: "Germany"}}, { id:3,Department: "Computer",Value : "100000",Title : "Security",ComID : "67", organisation:{ City:"Amsterdam",ComID:"67",Country: "Holland"}}]
Since d3-collection has been deprecated in favor of d3.array, we can use d3.rollups to achieve what used to work with d3.nest:
d3
.rollups(
data,
xs => d3.sum(xs, x => x.Value),
d => d.organisation.Country
)
.map(([k, v]) => ({ Country: k, Value: v }))
This:
Applies a d3.rollups:
d3.rollups takes 3 parameters: the input array, a reducing function and a grouping function
d => d.organisation.Country groups items by Country
xs => d3.sum(xs, x => x.Value) reduces grouped values by extracting their
Value and summing them using d3.sum
Formats the rollups' output in order to get the expected output (the map transformation).
var data = [
{ id: 0, Department: "Civil", Value: "40000", Title:"Sustainability", ComID: "45", organisation: { City: "New York", ComID: 45, Country: "USA" } },
{ id: 1, Department: "Energy", Value: "82000", Title: "Wind Energy", ComID: "62", organisation: { City: "Paris" , ComID: 62, Country: "France" } },
{ id: 2, Department: "Medical", Value: "67000", Title: "Neuroscience", ComID: "21", organisation: { City: "Berlin", ComID: 21, Country: "Germany" } },
{ id: 3, Department: "Computer", Value: "100000", Title: "Security", ComID: "67", organisation: { City: "Amsterdam", ComID: 67, Country: "Holland" } }
];
var output =
d3.rollups(
data,
xs => d3.sum(xs, x => x.Value),
d => d.organisation.Country
)
.map(([k, v]) => ({ Country: k, Value: v }))
console.log(output);
<script src="https://d3js.org/d3-array.v2.min.js"></script>