I am struggling to group by an array with it's sub-array. Here is my original array:
var people = [
{
name: "Bob",
age: "20",
car: {
colour: "Blue",
size: "Big",
rpm: "380"
}
},
{
name: "Mary",
age: "21",
car: {
colour: "Orange",
size: "Small",
rpm: "20"
}
},
{
name: "Janet",
age: "22",
car: {
colour: "Blue",
size: "Big",
rpm: "380"
}
}
];
And I would like to group by the colour of the car object into this form:
var cars = [
{
colour: "Blue",
size: "Big",
rpm: "380",
people: [{ name: "Bob", age: "20" }, { name: "Janet", age: "22" }]
},
{
colour: "Orange",
size: "Small",
rpm: "20",
people: [{ name: "Mary", age: "21" }]
}
];
I am relatively new to JavaScript so any help you can provide will be appreciated. Any external libraries are welcome.
EDIT
To answer #ths, I was able to successfully extract out the car object to the main array using this code:
resultArray = [];
people.forEach(function(people) {
let carObj = people.car;
console.log("TCL: carObj", carObj)
carObj["people"] = people;
resultArray.push(carObj);
});
But I had trouble combining the arrays into the different car types.
var people = [
{
name: "Bob",
age: "20",
car: {
colour: "Blue",
size: "Big",
rpm: "380"
}
},
{
name: "Mary",
age: "21",
car: {
colour: "Orange",
size: "Small",
rpm: "20"
}
},
{
name: "Janet",
age: "22",
car: {
colour: "Blue",
size: "Big",
rpm: "380"
}
}
];
let carMap = {};
people.forEach(p => {
const carKey = `${p.car.colour}-${p.car.size}-${p.car.rpm}`;
if (carKey in carMap) {
carMap[carKey].people.push({
name: p.name,
age: p.age
});
} else {
carMap[carKey] = {
...p.car,
people: [{
name: p.name,
age: p.age
}]
};
}
})
console.log(Object.values(carMap));
Create a car-people Map. Define a carkey that combined by the attribute of car. Finally, get values of the carMap.
The .reduce method can be used to transform arrays into objects with/arrays with a different structure:
var people = [
{
name: "Bob",
age: "20",
car: {
colour: "Blue",
size: "Big",
rpm: "380"
}
},
{
name: "Mary",
age: "21",
car: {
colour: "Orange",
size: "Small",
rpm: "20"
}
},
{
name: "Janet",
age: "22",
car: {
colour: "Blue",
size: "Big",
rpm: "380"
}
}
];
const generateId = (object) => Object.keys(object).map(key => key + object[key]).join('-');
var personByCar = Object.values(people.reduce((acc, person) => {
// Uniquely identify the car properties:
const carId = generateId(person.car);
// Add the people property to store related persons
if(!acc.hasOwnProperty(carId)){
acc[carId] = {...person.car, people: []};
}
// Add the person
acc[carId].people.push({person});
return acc;
}, {}));
console.log(personByCar)
More info about reduce: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce
Related
This is the data I am working with below.
const data = [
{
name: "Frank Blanchard",
gender: "male",
friends: [
{
name: "Corina Irwin",
gender: "female",
sub_friends: [
{
name: "Alyssa Shelton",
eyeColor: "brown",
gender: "female",
},
{
name: "Patrice Morton",
eyeColor: "blue",
gender: "female",
},
{
name: "Hazel Berry",
eyeColor: "blue",
gender: "female",
},
{
name: "Tricia Wells",
eyeColor: "blue",
gender: "female",
},
{
name: "Mendoza Patton",
eyeColor: "blue",
gender: "male",
},
],
},
{
name: "Jayne Boyd",
gender: "female",
sub_friends: [
{
name: "Jacobs Potter",
eyeColor: "blue",
gender: "male",
},
],
},
{
name: "Justine Fox",
gender: "female",
sub_friends: [
{
name: "Madeline Harrell",
eyeColor: "brown",
gender: "female",
},
{
name: "Simpson Pratt",
eyeColor: "blue",
gender: "male",
},
{
name: "Rachel Mooney",
eyeColor: "blue",
gender: "female",
},
],
},
],
},
{
name: "Ingrid Blackwell",
gender: "female",
friends: [
{
name: "Melody Carroll",
gender: "female",
sub_friends: [
{
name: "Sonja Gillespie",
eyeColor: "blue",
gender: "female",
},
],
},
{
name: "Herring Kaufman",
gender: "male",
sub_friends: [
{
name: "Kathy Pennington",
eyeColor: "blue",
gender: "female",
},
{
name: "Marisa Mckee",
eyeColor: "blue",
gender: "female",
},
{
name: "Gillespie Dyer",
eyeColor: "brown",
gender: "male",
},
{
name: "Aida Cantrell",
eyeColor: "blue",
gender: "female",
},
{
name: "Lucy Mcconnell",
eyeColor: "brown",
gender: "female",
},
],
},
],
},
{
name: "Isabelle Moon",
gender: "female",
friends: [
{
name: "Elnora Stone",
gender: "female",
sub_friends: [
{
name: "Collins Alford",
eyeColor: "brown",
gender: "male",
},
],
},
],
},
];
I want to go through to sub_friends and return all that match gender === male. My initial test was filtering to the sub_friends, which I then returned. But I am getting an empty array.
var filtered = data.filter(({ friends }) => {
friends.filter(({ sub_friends }) => {
return sub_friends.filter((element) => element.gender === "male");
});
});
console.log("LOG: ", filtered);
I also tried using map
var filtered = data.map(({ friends }) => {
friends.map(({ sub_friends }) => {
return sub_friends.filter((element) => element.gender === "male");
});
});
console.log("LOG: ", filtered);
I get
Array(3) [ undefined, undefined, undefined ]
The output I want is an array, that I can map, to display the names in sub_friends.
You can use flatMap and filter together to achieve your goal.
flatMap is to group all specific items into a new array.
From your data, you have a list of friends and then find all sub_friends to put them under the same array. The last part is filtering all info.gender === "male" from sub_friends.
//minified your data (not important)
const data=[{name:"Frank Blanchard",gender:"male",friends:[{name:"Corina Irwin",gender:"female",sub_friends:[{name:"Alyssa Shelton",eyeColor:"brown",gender:"female"},{name:"Patrice Morton",eyeColor:"blue",gender:"female"},{name:"Hazel Berry",eyeColor:"blue",gender:"female"},{name:"Tricia Wells",eyeColor:"blue",gender:"female"},{name:"Mendoza Patton",eyeColor:"blue",gender:"male"}]},{name:"Jayne Boyd",gender:"female",sub_friends:[{name:"Jacobs Potter",eyeColor:"blue",gender:"male"}]},{name:"Justine Fox",gender:"female",sub_friends:[{name:"Madeline Harrell",eyeColor:"brown",gender:"female"},{name:"Simpson Pratt",eyeColor:"blue",gender:"male"},{name:"Rachel Mooney",eyeColor:"blue",gender:"female"}]}]},{name:"Ingrid Blackwell",gender:"female",friends:[{name:"Melody Carroll",gender:"female",sub_friends:[{name:"Sonja Gillespie",eyeColor:"blue",gender:"female"}]},{name:"Herring Kaufman",gender:"male",sub_friends:[{name:"Kathy Pennington",eyeColor:"blue",gender:"female"},{name:"Marisa Mckee",eyeColor:"blue",gender:"female"},{name:"Gillespie Dyer",eyeColor:"brown",gender:"male"},{name:"Aida Cantrell",eyeColor:"blue",gender:"female"},{name:"Lucy Mcconnell",eyeColor:"brown",gender:"female"}]}]},{name:"Isabelle Moon",gender:"female",friends:[{name:"Elnora Stone",gender:"female",sub_friends:[{name:"Collins Alford",eyeColor:"brown",gender:"male"}]}]}];
//the main logic
const results = data.flatMap(info => info.friends).flatMap(info => info.sub_friends).filter(info => info.gender === "male")
console.log(results)
The reduce function can help here (see the result image below).
The output can be further adjusted to store all the values in single array:
let output = data.reduce(function(acc,curr) {
let male = curr.friends.reduce(function(acc1,curr1) {
acc1.push(curr1.sub_friends);
return acc1;
}, []);
acc.push(male);
return acc;
},[]);
let male = output.reduce(function(acc,curr) {
let male1 = curr.reduce(function(acc1,curr1) {
acc1.push(curr1.filter((x) => x.gender === "male"));
return acc1;
},[]);
acc.push(male1);
return acc;
},[])
console.log(male);
A simpler and Specified method would be this approach which you can use forEach method and make a nested loop over the object of array and push sub_feriend name into arr and after that filter the array to get just the names
let arr = [];
data.forEach(({friends}) =>{
friends.forEach(({sub_friends}) => {
arr.push(sub_friends.find(x => x.gender === "male"))
})
})
let names = arr.filter(x => x !== undefined).map(x => x.name)
console.log(names)
I have the data from the database and I want to push the duplicate value to each array. I attach the exact example I want.
// origin data
const data = [
{
name: "Amy",
age: 17,
},
{
name: "Amy",
age: 17,
},
{
name: "Amy",
age: 17,
},
{
name: "Tommy",
age: 20,
},
{
name: "Tommy",
age: 20,
},
];
//result that I want to get
arr1 = [
{
name: "Amy",
age: 17,
},
{
name: "Amy",
age: 17,
},
{
name: "Amy",
age: 17,
},
];
arr2 = [
{
name: "Tommy",
age: 20,
},
{
name: "Tommy",
age: 20,
},
];
I want to create an array based on the name in this data and push it. Can anyone help?
One way would be to use reduce together with Object.values:
const data = [
{
name: "Amy",
age: 17,
},
{
name: "Amy",
age: 17,
},
{
name: "Amy",
age: 17,
},
{
name: "Tommy",
age: 20,
},
{
name: "Tommy",
age: 20,
}
];
const result = Object.values(data.reduce((acc, cur) => {
const key = `${cur.name}:${cur.age}`;
const prev = acc[key] || [];
return {
...acc,
[key]: prev.concat(cur)
}
}, {}));
console.log(result);
For example if I have a multidimensional array like this:
const coWorkers = [
{ name: "Jack", age: 40, resident_state: "Wyoming" },
{ name: "Mary", age: 30, resident_state: "New Jersey" },
{ name: "Kate", age: 20, resident_state: "Florida" },
{ name: "Gerome", age: 50, resident_state: "Texas" },
];
and I want to create a function to turn one field in this array (ex: state) to lowercase, how would I go about doing so?
I have tried this:
const coWorkers = [
{ name: "Jack", age: 40, resident_state: "Wyoming" },
{ name: "Mary", age: 30, resident_state: "New Jersey" },
{ name: "Kate", age: 20, resident_state: "Florida" },
{ name: "Gerome", age: 50, resident_state: "Texas" },
];
function lowerCaseStates(coWorkers.resident_state){
lowerCasing = function() {
return coWorkers.resident_state.toLowerCase();
}
lowercaseStates = coWorkers.map(lowerCasing);
console.log(lowercaseStates)
}
lowerCaseStates()
How would I go about doing this while not replacing each cell in the array individually?
You cannot have a dotted parameter in the function (and do not need it if you access it inside the function
just run directly on the string
You do not have a multidimensional array. You have an object array
You can use a function to process the complete array in one map statement - I use the ...rest to just copy the other items we are not interested in
Note I have to wrap the object in () if I do not have => {} in the arrow function
const coWorkers = [
{ name: "Jack", age: 40, resident_state: "Wyoming" },
{ name: "Mary", age: 30, resident_state: "New Jersey" },
{ name: "Kate", age: 20, resident_state: "Florida" },
{ name: "Gerome", age: 50, resident_state: "Texas" }
];
const lowerCase = ({resident_state, ...rest }) =>
({ resident_state: resident_state.toLowerCase(), ...rest });
const coWorkersLowerCase = coWorkers
.map(lowerCase)
console.log(coWorkersLowerCase)
If you want to modify the original, you can use a forEach
const coWorkers = [
{ name: "Jack", age: 40, resident_state: "Wyoming" },
{ name: "Mary", age: 30, resident_state: "New Jersey" },
{ name: "Kate", age: 20, resident_state: "Florida" },
{ name: "Gerome", age: 50, resident_state: "Texas" }
];
coWorkers.forEach(item => item.resident_state = item.resident_state.toLowerCase())
console.log(coWorkers)
Hi i would go like this
coWorkers.map(x=>{
x.resident_state=x.resident_state.toLowerCase();
return x
})
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.
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));