Recursively filtering array with nested objects - javascript

I have some data that looks like :
{
_id: "5e985a07feddae7617ac44f6",
age: 24,
eyeColor: "brown",
name: "Cummings Baxter",
gender: "male",
company: "VELOS",
email: "cummingsbaxter#velos.com",
phone: "+1 (907) 482-2451",
tags: ["labore", "elit", "excepteur", "nisi", "mollit", "anim", "aliquip"],
friends: [
{
id: 0,
name: "Sheppard Jensen",
},
],
},
{
_id: "5e985a0709dfa1e6fd93c6ad",
age: 32,
eyeColor: "brown",
name: "Madelyn Dickson",
gender: "female",
company: "KENGEN",
email: "madelyndickson#kengen.com",
phone: "+1 (984) 521-2439",
tags: ["nisi", "veniam", "dolore", "officia", "ex", "non", "pariatur"],
friends: [
{
id: 0,
name: "Bruce Barton",
},
{
id: 1,
name: "Juliet Schmidt",
},
{
id: 2,
name: "Horton Haley",
},
{
id: 3,
name: "Herminia Witt",
},
],
},
{
_id: "5e985a0737e2306e9aef6ecd",
age: 26,
eyeColor: "blue",
name: "Mcguire Mercado",
gender: "male",
company: "LINGOAGE",
email: "mcguiremercado#lingoage.com",
phone: "+1 (963) 450-2194",
tags: ["cupidatat", "occaecat", "amet", "qui", "elit", "esse", "deserunt"],
friends: [
{
id: 0,
name: "Loraine Harper",
},
{
id: 1,
name: "Luann Randall",
},
{
id: 2,
name: "Obrien Rich",
},
{
id: 3,
name: "Noble Wilkerson",
},
],
},
{
_id: "5e985a07148cfba58c860ec2",
age: 26,
eyeColor: "brown",
name: "Marina Porter",
gender: "female",
company: "GORGANIC",
email: "marinaporter#gorganic.com",
phone: "+1 (867) 417-3497",
tags: [
"laborum",
"aliquip",
"sit",
"adipisicing",
"aute",
"cupidatat",
"aliquip",
],
friends: [
{
id: 0,
name: "Blair Hill",
},
{
id: 1,
name: "Ebony Jimenez",
},
],
},
{
_id: "5e985a074984f9f08ccaaa4c",
age: 255,
eyeColor: "green",
name: "Barlow Ferguson",
gender: "male",
company: "TOYLETRY",
email: "barlowferguson#toyletry.com",
phone: "+1 (837) 484-2231",
tags: ["est", "dolor", "minim", "ut", "anim", "culpa", "non"],
friends: [
{
id: 0,
name: "Delacruz Acevedo",
},
{
id: 1,
name: "Gloria Tanner",
},
{
id: 2,
name: "Cantrell Myers",
},
{
id: 3,
name: "Fisher Leonard",
},
{
id: 3,
name: "Gloria Tenner",
},
],
},
];
I want to write a function that recursively filters for desired word and returns object which contains that word.
example : function filterWith(data, "Sheppard Jensen") would return
_id: "5e985a07feddae7617ac44f6",
age: 24,
eyeColor: "brown",
name: "Cummings Baxter",
gender: "male",
company: "VELOS",
email: "cummingsbaxter#velos.com",
phone: "+1 (907) 482-2451",
tags: ["labore", "elit", "excepteur", "nisi", "mollit", "anim", "aliquip"],
friends: [
{
id: 0,
name: "Sheppard Jensen",
},
],
},
I could do this non-recursively but since resursive way could be much more efficient I want to know the way to do this. Would really apreciate any help.

here is a simple way , because JSON.stringify itself use recursively way
function filterWith(data, str) {
return data.filter(each => JSON.stringify(each).indexOf(str) > -1)
}
but you would like to do it by yourself, you can try this way
function filterWith(data, str) {
const isTarget = (_, str) => _.indexOf(str) > -1;
const $filterWith = ($data) => {
if ($data === undefined || $data === null) {
return false;
}
if (typeof $data != 'object' ) {
return isTarget(`${$data}`, `${str}`)
}
if (Array.isArray($data)) {
for (let i of $data) {
if ($filterWith($data[i])) return true
}
}
for (let i in $data) {
if (isTarget(`${data}`, `${i}`) || $filterWith($data[i])) return true
}
return false
}
return data.filter(each => $filterWith(each))
}

I would write a fairly simple recursive check that a given object contains a the string and then write a trivail filter on top of this.
const hasString = (str) => (obj) =>
Array.isArray (obj)
? obj .some (hasString (str))
: Object (obj) === obj
? hasString (str) (Object. values (obj))
: typeof obj === 'string'
? obj .includes (str)
: false
const filterWith = (xs, str) =>
xs .filter (hasString (str))
const input = [{_id: "5e985a07feddae7617ac44f6", age: 24, eyeColor: "brown", name: "Cummings Baxter", gender: "male", company: "VELOS", email: "cummingsbaxter#velos.com", phone: "+1 (907) 482-2451", tags: ["labore", "elit", "excepteur", "nisi", "mollit", "anim", "aliquip"], friends: [{id: 0, name: "Sheppard Jensen"}]}, {_id: "5e985a0709dfa1e6fd93c6ad", age: 32, eyeColor: "brown", name: "Madelyn Dickson", gender: "female", company: "KENGEN", email: "madelyndickson#kengen.com", phone: "+1 (984) 521-2439", tags: ["nisi", "veniam", "dolore", "officia", "ex", "non", "pariatur"], friends: [{id: 0, name: "Bruce Barton"}, {id: 1, name: "Juliet Schmidt"}, {id: 2, name: "Horton Haley"}, {id: 3, name: "Herminia Witt"}]}, {_id: "5e985a0737e2306e9aef6ecd", age: 26, eyeColor: "blue", name: "Mcguire Mercado", gender: "male", company: "LINGOAGE", email: "mcguiremercado#lingoage.com", phone: "+1 (963) 450-2194", tags: ["cupidatat", "occaecat", "amet", "qui", "elit", "esse", "deserunt"], friends: [{id: 0, name: "Loraine Harper"}, {id: 1, name: "Luann Randall"}, {id: 2, name: "Obrien Rich"}, {id: 3, name: "Noble Wilkerson"}]}, {_id: "5e985a07148cfba58c860ec2", age: 26, eyeColor: "brown", name: "Marina Porter", gender: "female", company: "GORGANIC", email: "marinaporter#gorganic.com", phone: "+1 (867) 417-3497", tags: ["laborum", "aliquip", "sit", "adipisicing", "aute", "cupidatat", "aliquip"], friends: [{id: 0, name: "Blair Hill"}, {id: 1, name: "Ebony Jimenez"}]}, {_id: "5e985a074984f9f08ccaaa4c", age: 255, eyeColor: "green", name: "Barlow Ferguson", gender: "male", company: "TOYLETRY", email: "barlowferguson#toyletry.com", phone: "+1 (837) 484-2231", tags: ["est", "dolor", "minim", "ut", "anim", "culpa", "non"], friends: [{id: 0, name: "Delacruz Acevedo"}, {id: 1, name: "Gloria Tanner"}, {id: 2, name: "Cantrell Myers"}, {id: 3, name: "Fisher Leonard"}, {id: 3, name: "Gloria Tenner"}]}]
console .log (filterWith (input, 'Sheppard Jensen'))
.as-console-wrapper {max-height: 100% !important; top: 0}
hasString checks if the input is an array, and if it is, simply recurs over its children until it finds a match, returning false if none match. If the input is an object, we do the same thing with its keys. If the input is a string, we see if it includes the target value. (You might prefer an equality check here.) And if it's not a string, object, or array, it returns false.
filterWith is a simple wrapper that filters an input array using hasString.

Related

How can I filter this data and return an array matching the condition in JavaScript?

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)

Check if all relevant sub-arrays match criteria

There is a thatre establishment that has a number of theatre groups. Each groups is either international or not. For some reason it is necessary that each international group has at least one female actor , aka. actress. If there exists even one international group without any female actor, function checkGenderEquality must return false.
Theatre: [
{
groupNmae: 'Medea',
id: 1001,
international: false,
actors: [
{
firstName: 'Vilfrid',
birthDate: '1981-01-01',
gender: 'Male',
},
{
firstName: 'Nils',
birthDate: '1973-10-11',
gender: 'Male'
},
{
firstName: 'Valentina',
birthDate: '2001-05-09',
gender: 'Female'
}
]
}
,
{
groupNmae: 'Hamlet',
id: 2001,
international: true,
actors: [
{
firstName: 'Robin',
birthDate: '1999-07-20',
gender: 'Male'
},
{
firstName: 'Johannes',
birthDate: '1998-12-13',
gender: 'Male'
},
{
firstName: 'Ludwig',
birthDate: '1965-03-22',
gender: 'Male'
}
]
},
{
groupNmae: 'King Lear',
id: 3001,
international: true,
actors: [
{
firstName: 'Kristina',
birthDate: '1977-04-19',
gender: 'Female'
},
{
firstName: 'Pernilla',
birthDate: '1974-02-03',
gender: 'Female'
},
{
firstName: 'Bruno',
birthDate: '1970-02-23',
gender: 'Male'
}
]
},
{
groupNmae: 'Medea',
id: 4001,
international: false,
actors: [
{
firstName: 'Alfons',
birthDate: '1986-11-01',
gender: 'Male',
},
{
firstName: 'Ulrik',
birthDate: '1979-11-01',
gender: 'Male'
},
{
firstName: 'Oskar',
birthDate: '2000-10-10',
gender: 'Male'
}
]
}
]
public checkGenderEquality(theatreGroup: any[]) {
let equality = theatreGroup.every(({ t }) =>
(
(t.international != false) || (t.some((a) => a.gender == 'Female')))
);
return equality;
}
Currently checkGenderEquality returns always true. How can I modify to get the correct result (which in this case is false; becuse Hamlet is an international group but has no female actor)?
If the group.international === false, you don't need to check the actors. if the group.international === true, you need to check actors.
Updated: replace filter with every and some
const theatre = [
{
groupNmae: "Medea",
id: 1001,
international: true,
actors: [
{
firstName: "Valentina",
birthDate: "2001-05-09",
gender: "Female",
},
],
},
{
groupNmae: "Hamlet",
id: 2001,
international: true,
actors: [
{
firstName: "Ludwig",
birthDate: "1965-03-22",
gender: "Male",
},
],
},
];
const isEquality = theatre.every(
item =>
(item.international === true &&
item.actors.some(actor => actor.gender === "Female")) ||
item.international === false
);
console.log(isEquality);

javascript - add object at the beginning on each map iteration

So, let's suppose I have this:
var officers = [
{ id: 20, name: 'Captain', lastName: 'Piett' },
{ id: 24, name: 'General', lastName: 'Veers' },
{ id: 56, name: 'Admiral', lastName: 'Ozzel' },
{ id: 88, name: 'Commander', lastName: 'Jerjerrod' }
];
If I do this:
var officersIds = officers.map(x => [x.name, x.lastName]);
the result of officersIds is gonna be this:
[ "Captain", "Piett" ], [ "General", "Veers" ], [ "Admiral", "Ozzel" ], [ "Commander", "Jerjerrod" ]
right?
So, what I need to do is to put an object on each map iteration so the output is now this, for example:
[["x", "y"], [ "Captain", "Piett" ]],
[["x", "y"], [ "General", "Veers" ]],
[["x", "y"], [ "Admiral", "Ozzel" ]],
[["x", "y"], [ "Commander", "Jerjerrod" ]]
Why do I need this? Better don't ask ;) But it's a complex problem and if you help me solve this simple one I could transfer your solution to my complex problem.
IMPORTANT: Is there a way to do this in one line?
If you simply want to add ['x', 'y'] to the start of the array, you can use the spread operator to make it a one-liner:
var officersIds = [['x', 'y'], ...officers.map(x => [x.name, x.lastName])];
See proof-of-concept below:
var officers = [
{ id: 20, name: 'Captain', lastName: 'Piett' },
{ id: 24, name: 'General', lastName: 'Veers' },
{ id: 56, name: 'Admiral', lastName: 'Ozzel' },
{ id: 88, name: 'Commander', lastName: 'Jerjerrod' }
];
var officersIds = [['x', 'y'], ...officers.map(x => [x.name, x.lastName])];
console.log(officersIds);
However, if you want to add ['x', 'y'] to each item in the array, then you should do this instead:
var officersIds = officers.map(x => [['x', 'y'], [x.name, x.lastName]]);
var officers = [
{ id: 20, name: 'Captain', lastName: 'Piett' },
{ id: 24, name: 'General', lastName: 'Veers' },
{ id: 56, name: 'Admiral', lastName: 'Ozzel' },
{ id: 88, name: 'Commander', lastName: 'Jerjerrod' }
];
var officersIds = officers.map(x => [['x', 'y'], [x.name, x.lastName]]);
console.log(officersIds);
var officers = [
{ id: 20, name: 'Captain', lastName: 'Piett' },
{ id: 24, name: 'General', lastName: 'Veers' },
{ id: 56, name: 'Admiral', lastName: 'Ozzel' },
{ id: 88, name: 'Commander', lastName: 'Jerjerrod' },
];
officers.unshift({ name: x, lastName: y });
var officersIds = officers.map(x => [x.name, x.lastName]);
Or
var officers = [
{ id: 20, name: 'Captain', lastName: 'Piett' },
{ id: 24, name: 'General', lastName: 'Veers' },
{ id: 56, name: 'Admiral', lastName: 'Ozzel' },
{ id: 88, name: 'Commander', lastName: 'Jerjerrod' },
];
var officersIds = officers.map(x => [x.name, x.lastName]);
officersIds.unshift(["x", "y" ]);
You could just add the wanted parts to the mapping function.
var officers = [{ id: 20, name: 'Captain', lastName: 'Piett' }, { id: 24, name: 'General', lastName: 'Veers' }, { id: 56, name: 'Admiral', lastName: 'Ozzel' }, { id: 88, name: 'Commander', lastName: 'Jerjerrod' }],
officersIds = officers.map(x => [["x", "y"], [x.name, x.lastName]]);
console.log(officersIds);
Use Map over officers.
var officers = [
{ id: 20, name: 'Captain', lastName: 'Piett' },
{ id: 24, name: 'General', lastName: 'Veers' },
{ id: 56, name: 'Admiral', lastName: 'Ozzel' },
{ id: 88, name: 'Commander', lastName: 'Jerjerrod' }
];
var officersIds = officers.map(x => [["x", "y"], [x.name, x.lastName]]);
console.log(officersIds);

How to merge the property with same key in two object array?

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

Conditional wrap items [duplicate]

How can I group data in Angular 2 with TypeScript. I am aware that this is done using "group by" filter in Angular 1.X, but not getting how to group data in Angular 2. I have this array:
import {Employee} from './employee';
export var employees: Employee[];
employees = [
{ id: 1, firstName: "John", lastName: "Sonmez", department: 1, age: 24, address: "24/7, Working hours apartment, Cal. US", contactNumber: "+968546215789" },
{ id: 2, firstName: "Mark", lastName: "Seaman", department: 2, age: 25, address: "32-C, Happy apartments, Block-9C, Cal. US", contactNumber: "+968754216984" },
{ id: 3, firstName: "Jamie", lastName: "King", department: 3, age: 32, address: "54/II, Glorydale apartment, Cal. US", contactNumber: "+967421896326" },
{ id: 5, firstName: "Jacob", lastName: "Ridley", department: 5, age: 24, address: "24/7, Working hours apartment, Cal. US", contactNumber: "+968546215789" },
{ id: 6, firstName: "Peter", lastName: "Parker", department: 3, age: 25, address: "32-C, Happy apartments, Block-9C, Cal. US", contactNumber: "+968754216984" },
{ id: 7, firstName: "Martin", lastName: "Luther", department: 4, age: 32, address: "54/II, Glorydale apartment, Cal. US", contactNumber: "+967421896326" },
{ id: 8, firstName: "Raghav", lastName: "Kumar", department: 1, age: 34, address: "51/C Shivalik, Cal. US", contactNumber: "+967842569842" },
{ id: 9, firstName: "Narayan", lastName: "Sonmez", department: 3, age: 24, address: "24/7, Working hours apartment, Cal. US", contactNumber: "+968546215789" },
{ id: 10, firstName: "Russell", lastName: "Andre", department: 2, age: 25, address: "32-C, Happy apartments, Block-9C, Cal. US", contactNumber: "+968754216984" },
{ id: 11, firstName: "Ramona", lastName: "King", department: 4, age: 32, address: "54/II, Glorydale apartment, Cal. US", contactNumber: "+967421896326" },
{ id: 12, firstName: "Andre", lastName: "Russell", department: 1, age: 34, address: "51/C Shivalik, Cal. US", contactNumber: "+967842569842" },
{ id: 13, firstName: "Nathan", lastName: "Leon", department: 1, age: 24, address: "24/7, Working hours apartment, Cal. US", contactNumber: "+968546215789" },
{ id: 14, firstName: "Brett", lastName: "Lee", department: 5, age: 25, address: "32-C, Happy apartments, Block-9C, Cal. US", contactNumber: "+968754216984" },
{ id: 15, firstName: "Tim", lastName: "Cook", department: 2, age: 32, address: "54/II, Glorydale apartment, Cal. US", contactNumber: "+967421896326" },
{ id: 16, firstName: "Steve", lastName: "Jobs", department: 5, age: 34, address: "51/C Shivalik, Cal. US", contactNumber: "+967842569842" }
];
and I am looking to count the employees by department, like
Department 1 has 4 employees
and so on.
Joining the department id with actual department (so that I can get the department name) is another story I need to figure out.
I would create a custom pipe to do that as described below:
#Pipe({name: 'groupBy'})
export class GroupByPipe implements PipeTransform {
transform(value: Array<any>, field: string): Array<any> {
const groupedObj = value.reduce((prev, cur)=> {
(prev[cur[field]] = prev[cur[field]] || []).push(cur);
return prev;
}, {});
return Object.keys(groupedObj).map(key => ({ key, value: groupedObj[key] }));
}
}
And then on your template you can write:
<div *ngFor="let item of employees | groupBy: 'department'">
Department {{ item.key }} has {{ item.value.length }} employees
</div>
See also corresponding plunker https://plnkr.co/edit/49fWY1rMbSZtNQ3H
You can use ngx-pipes https://github.com/danrevah/ngx-pipes#groupby
this.arrayObject = [
{id: 1, elm: 'foo', value: 0},
{id: 2, elm: 'bar', value: 1},
{id: 3, elm: 'foo', value: 2},
{id: 4, elm: 'foo', value: 2}
];
this.arrayNestedObject = [
{id: 1, prop: { deep: 'foo' }},
{id: 2, prop: { deep: 'bar' }},
{id: 3, prop: { deep: 'foo' }},
{id: 4, prop: { deep: 'bar' }}
];
<p>{{ arrayObject | groupBy: 'elm' }}</p>
<!-- Output: "{foo: [{id: 1, elm: 'foo', value: 0}, {id: 3, elm: 'foo', value: 2}, {id: 4, elm: 'foo', value: 2}], bar: [{id: 2, elm: 'bar', value: 1}]}" -->
var dept = employees.map((m) => m.department).filter((f, i, ar) => ar.indexOf(f) === i);
console.log(dept);
var group = employees.reduce((accumulator, item, i, arr) => {
if (dept.length) {
var pop = dept.shift();
var list = arr.filter((f) => f.department == pop);
accumulator.push(...list);
}
return accumulator;
}, []);
console.log(group);
If you need to access nested properties or need to compare objects you can do
#Pipe({ name: 'groupByProperty' })
export class GroupByPropertyPipe implements PipeTransform {
transform(value: Array<any>, property: string): Array<any> {
if (!value) {
return null;
}
const group = value.reduce((previous, current) => {
const parts = property.split('.');
let currentValue: any;
parts.forEach(part => {
currentValue = currentValue ? currentValue[part] : current[part];
});
// Stringify objects for comparison
currentValue = typeof currentValue === 'object' ? JSON.stringify(currentValue) : currentValue;
if (!previous[currentValue]) {
previous[currentValue] = [current];
} else {
previous[currentValue].push(current);
}
return previous;
}, {});
return Object.keys(group).map(key => ({ key, value: group[key] }));
}
}

Categories

Resources