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)
This question already has answers here:
Extract certain properties from all objects in array
(5 answers)
Closed 2 years ago.
let arr = [
{ name: "rick", age: "44", phone: "33434", address: "florida" },
{ name: "sam", age: "33", phone: "23232", address: "milan" }
]
I only want to show only name and age, like this:
let filteredArr = [
{ name: "rick", age: "44"},
{ name: "sam", age: "33"}
]
With map
let arr = [
{ name: "rick", age: "44", phone: "33434", address: "florida" },
{ name: "sam", age: "33", phone: "23232", address: "milan" }
]
const newarr = arr.map(({name, age}) => ({name, age}))
console.log(newarr)
Based on https://stackoverflow.com/a/47916931/476951, you can do this dynamically
const arr = [
{ name: "rick", age: "44", phone: "33434", address: "florida" },
{ name: "sam", age: "33", phone: "23232", address: "milan" }
]
function filterMap (arr, fields) {
return arr.map(el =>
fields.reduce((a,c) => ({...a, [c]: el[c]}), {}))
}
const newarr = filterMap(arr, ['name', 'age'])
console.log(newarr)
In this case you map opt to use map instead of filter
let arr = [{
name: "rick",
age: "44",
phone: "33434",
address: "florida"
},
{
name: "sam",
age: "33",
phone: "23232",
address: "milan"
}
]
let filteredData = arr.map(item => {
return {
name: item.name,
age: item.age
}
});
console.log(filteredData)
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);
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 am trying to determine the best data-type to sort an array of objects, into groups defined by properties. I have an array of objects, like so:
var people = [
{
name: 'Pete',
gender: 'Male',
age: 22
},
{
name: 'Samantha',
gender: 'Female',
age: 20
},
{
name: 'Frank',
gender: 'Male',
age: 22
},
{
name: 'Gary',
gender: 'Male',
age: 21
},
{
name: 'Maria',
gender: 'Female',
age: 20
},
{
name: 'Hannah',
gender: 'Female',
age: 21
},
{
name: 'Pete',
gender: 'Male',
age: 20
}
];
I need to group these objects into an arbitrary-defined group. E.g.:
Group 1: gender
Group 2: age
(This can be defined by the server and can change to contain a third group if we wish.)
Which then gives me (visually):
Male:
21:
Gary
22:
Pete
Frank
Female
20:
Samantha
Maria
21:
Hannah
I think the appropriate data type would be an object of objects. I.e.:
{
Male: {
21: {
[
{
name: 'Gary',
gender: 'Male',
age: 21
}
]
},
22: {
[
{
name: 'Pete',
gender: 'Male',
age: 22
},
{
name: 'Frank',
gender: 'Male',
age: 22
}
]
}
},
Female: {
20: {
[
{
name: 'Samantha',
gender: 'Female',
age: 20
},
{
name: 'Maria',
gender: 'Female',
age: 20
}
]
},
21: {
[
{
name: 'Hannah',
gender: 'Female',
age: 21
}
]
}
}
}
But I cannot work out, for the life of me, an appropriate algorithm to sort these objects into a data-type which represents the above.
There is a useful utility in underscore.js called _.groupBy(arr, callback) which I can use as follows:
_.groupBy(people, function (person) {
var props = ['gender', 'age'], // server-defined
prop = [];
for (var i = 0, length = props.length; i < length; i++) {
prop.push(person[props[i]]);
}
return prop.join('|');
});
This gives me a 1-depth object which I can use a for...in loop to iterate over the keys, and loop through to build the object above, but it's that bit of the code I'm stuck on.
The returned object would be:
{
"Male|22": [
{
"name": "Pete",
"gender": "Male",
"age": 22
},
{
"name": "Frank",
"gender": "Male",
"age": 22
}
],
"Female|20": [
{
"name": "Samantha",
"gender": "Female",
"age": 20
},
{
"name": "Maria",
"gender": "Female",
"age": 20
}
],
"Male|21": [
{
"name": "Gary",
"gender": "Male",
"age": 21
}
],
"Female|21": [
{
"name": "Hannah",
"gender": "Female",
"age": 21
}
],
"Male|20": [
{
"name": "Pete",
"gender": "Male",
"age": 20
}
]
}
I'm thinking of then looping through each key in the object, splitting at the pipe (|) and using recursion to construct a new object of objects containing the groups/array of data.
That's such a horrendous way of achieving this but I have no idea how to do it otherwise.
Is there a better way I'm missing?
Maybe this helps you. It utilises an array with the properties of the object and the result is grouped by the content of the properties.
The forEach loop iterates over the data. The reduce loop is for generating grouped properties for every given group and if it is the last one it returns an array, if not already there.
The last step is to push the value of one of people to the array.
var people = [{ name: 'Pete', gender: 'Male', age: 22 }, { name: 'Samantha', gender: 'Female', age: 20 }, { name: 'Frank', gender: 'Male', age: 22 }, { name: 'Gary', gender: 'Male', age: 21 }, { name: 'Maria', gender: 'Female', age: 20 }, { name: 'Hannah', gender: 'Female', age: 21 }, { name: 'Pete', gender: 'Male', age: 20 }],
groups = ['gender', 'age'],
grouped = {};
people.forEach(function (a) {
groups.reduce(function (o, g, i) { // take existing object,
o[a[g]] = o[a[g]] || (i + 1 === groups.length ? [] : {}); // or generate new obj, or
return o[a[g]]; // at last, then an array
}, grouped).push(a);
});
document.write('<pre>' + JSON.stringify(grouped, 0, 4) + '</pre>');
you can use lodash
groupBy(people, (item) => item.age + item.gender);
I realy don't understand why people always use frameworks for that kind of things.
Vanilla is faster and this is not too hard to code...
var people = [ { name: 'Pete', gender: 'Male', age: 22 }, { name: 'Samantha', gender: 'Female', age: 20 }, { name: 'Frank', gender: 'Male', age: 22 }, { name: 'Gary', gender: 'Male', age: 21 }, { name: 'Maria', gender: 'Female', age: 20 }, { name: 'Hannah', gender: 'Female', age: 21 }, { name: 'Pete', gender: 'Male', age: 20 }];
var grouped = {}; // final object
for (var i=0,len=people.length,p;i<len;i++) { // faster than .forEach
p = people[i];
if (grouped[p.gender] === undefined) // twice faster then hasOwnProperty
grouped[p.gender] = {};
if (grouped[p.gender][p.age] === undefined)
grouped[p.gender][p.age] = [];
grouped[p.gender][p.age].push(p); // your groupby is HERE xD
}
document.getElementById('grouped').innerHTML = JSON.stringify(grouped, null, 2)
<pre id="grouped"></pre>
I'm going to disagree with you about your expected output. I think you should just have an object with Male and Female array properties and use this code to populate the arrays:
var obj = people.reduce(function (p, c, i) {
p[c.gender].push(c);
return p;
}, { Male: [], Female: [] });
If you want to then filter those arrays you can write functions like this:
function getMaleByAge(obj, age) {
return obj.Male.filter(function (el) {
return el.age === age;
})
}
getMaleByAge(obj, 21); // { name: "Gary", gender: "Male", age: 21 }
DEMO
Using _.groupBy & _.map
var output = _.map(_.groupBy(people, 'gender'),function(obj, key){
var temp = {};
temp[key] = _.groupBy(obj, 'age')
return temp;
});
console.log(JSON.stringify(output, null,' '))
Will give following output
[{
"Male": {
"20": [
{
"name": "Pete",
"gender": "Male",
"age": 20
}
],
"21": [
{
"name": "Gary",
"gender": "Male",
"age": 21
}
],
"22": [
{
"name": "Pete",
"gender": "Male",
"age": 22
},
{
"name": "Frank",
"gender": "Male",
"age": 22
}
]
}
},
{
"Female": {
"20": [
{
"name": "Samantha",
"gender": "Female",
"age": 20
},
{
"name": "Maria",
"gender": "Female",
"age": 20
}
],
"21": [
{
"name": "Hannah",
"gender": "Female",
"age": 21
}
]
}
}
]