Javascript multilevel object spread operator - javascript

I have an array of objects, I want all data out from all objects into multiple arrays
const obj = [
{
school: {
name: 'abc',
students: [
{
class: 'A',
name: 'jhon',
},
{
class: 'B',
name: 'Doe',
},
],
},
},
{
school: {
name: 'XYZ',
students: [
{
class: 'C',
name: 'Cena',
},
{
class: 'B',
name: 'Big show',
},
],
},
},
];
I want result something like
[ ["abc", "xyz"], ["A", "B", "C", "B"], ["jhon", "Doe", "Cena", "Big show"]]
Any help will be appreciated

You can make use of reduce and take Object.values of it. While traversing the student array you can take Object.entries of Object. Here is a working example:
var obj = [ { school: { name: 'abc', students: [ { class: 'A', name: 'jhon', }, { class: 'B', name: 'Doe', }, ], }, }, { school: { name: 'XYZ', students: [ { class: 'C', name: 'Cena', }, { class: 'B', name: 'Big show', }, ], }, }];
var result = Object.values(obj.reduce((acc, {school})=>{
acc['schoolname'] = [...(acc['schoolname'] || []), school.name];
school.students.forEach(s=>{
Object.entries(s).forEach(([k,v])=>{
acc[k] = [...(acc[k] || []), v];
});
});
return acc;
},{}));
console.log(result);

Even though this is already answered, I want to share my approach.
It you are able to add a dependency, JSONata is well worth, and it can be used not only to query but also to transform objects with fairly simple queries, that most of the time are way easier to read and understand than regular reducers or maps.
Using JSONata your code will look like this:
jsonata('[ [ $.school.name], [ $.school.students.class ], [ $.school.students.name ] ]').evaluate(obj);
https://try.jsonata.org/zqqQNjYmx

Using the builtin Array methods.
schoolNames =obj.map(function(school){return school.school.name})
students = obj.map(function(school){return school.school.students}).flat()
studentNames = students.map(function(student){return student.name})
studentClass = students.map(function(student){return student.class})

Related

How to add elements to array in object property

I have the following array and object I would like to 'match'
const items = [
{ key: 1, name: 'A', owner: 'Alex', },
{ key: 2, name: 'B', owner: 'Barb', },
{ key: 3, name: 'C', owner: 'John', },
{ key: 4, name: 'D', owner: 'Barb', },
{ key: 5, name: 'E', owner: 'Alex', },
];
const owners = {
'Alex': { 1: [], 5: [] },
'John': { 3: [], },
'Barb': { 2: [], 4: [] },
}
I would like to have the following end result:
const ownersWithName = {
'Alex': [{ key: 1, name: 'A', }, { key: 5, name: 'E' }],
'Barb': [{ key: 2, name: 'B', }, { key: 4, name: 'D' }],
'John': [{ key: 3, name: 'C', }, ],
}
So far my solution is this:
function matchOwners (items, owners) {
const ownersWithName = {};
for (const item of items) {
if (owners[item.owner]) {
if (ownersWithName[item.owner]) {
ownersWithName[item.owner] = [ ...ownersWithName[item.owner], item];
} else {
ownersWithName[item.owner] = [item];
}
}
}
return ownersWithName;
}
This solution works, but i feel it's too verbose. i tried to use the spread operator without the if condition, but this needs the array to exist already, otherwise i get the error ownersWithName[item.owner] is not iterable. Is there a better way to do this?
Something like (completely untested):
ownersWithName = items.reduce((result, item) => {
if (owners[item.owner]) {
if (!(item.owner in result)) {
result[item.owner] = [];
}
result[item.owner].push({key: item.key, name: item.name});
}
return result;
}, {})
You can also simply achieve this by using Array.forEach() along with the Object.keys() method.
Live Demo (Descriptive comments has been added in the below code snippet) :
// Input array
const items = [
{ key: 1, name: 'A', owner: 'Alex', },
{ key: 2, name: 'B', owner: 'Barb', },
{ key: 3, name: 'C', owner: 'John', },
{ key: 4, name: 'D', owner: 'Barb', },
{ key: 5, name: 'E', owner: 'Alex', },
];
// Input object which should be used to match.
const owners = {
'Alex': { 1: [], 5: [] },
'John': { 3: [], },
'Barb': { 2: [], 4: [] },
};
// Declare an object which will store the final result.
const resultObj = {};
// Iterating over an items array to manipulate the data and make the final result set.
items.forEach(obj => {
resultObj[obj.owner] = !resultObj[obj.owner] ? [] : resultObj[obj.owner];
Object.keys(owners[obj.owner]).forEach(key => {
if (key == obj.key) {
resultObj[obj.owner].push({
key: obj.key,
name: obj.name
});
}
});
});
// Final output
console.log(resultObj);

Lodash how to group array of objects by array of values?

I want to group an array of objects based on a property which has an array of values,and i want to return a group for each individual value,not only for the whole array.
For example :
let crew = [
{
name:"john",
job :["electrician","carpenter"]
},
{
name: "bill",
job: ["electrician"]
},
{
name: "mark",
job: [ "carpenter"]
}
]
let groupedCrew = _.groupBy(crew,"job")
console.log(groupedCrew)
/*
carpenter:
[
{
job:
[
carpenter
],
name:
"mark"
}
],
electrician:
[
{
job:
[
"electrician"
],
name:
"bill"
}
],
electrician, carpenter:
[
{
job:
[
"electrician",
"carpenter"
],
name:
"john"
}
]
}
*/
In this example i want "john" to also appear in "electrician" group.
Any ideas ?
Once again let's group something using reduce
Here's the basic structure (plus the solution)
let crew = [{
name: "john",
job: ["electrician", "carpenter"]
},
{
name: "bill",
job: ["electrician"]
},
{
name: "mark",
job: ["carpenter"]
}
];
var obj = crew.reduce(function(agg, item) {
// grouping logic below this line
item.job.forEach(function(job) {
agg[job] = agg[job] || []
// agg[job].push (item);
// lets push only name so we can see output
agg[job].push(item.name)
})
// grouping logic above this line
return agg
}, {})
console.log(obj)
use custom .reduce() function
there is no need for lodash
const crew = [
{
name: 'john',
job: ['electrician', 'carpenter'],
},
{
name: 'bill',
job: ['electrician'],
},
{
name: 'mark',
job: ['carpenter'],
},
];
const groupedCrew = crew.reduce((groupedCrew, person) => {
person.job.forEach(job => {
if (!groupedCrew[job]) groupedCrew[job] = [];
groupedCrew[job].push(person);
});
return groupedCrew;
}, {});
console.log(JSON.stringify(groupedCrew, null, 4));

How to flat array of objects which can contain nested objects in my case

I have a data with structure like this:
const arr1 = [
{
name: 'a',
subObjects: [
{ name: 'a1', age: 10 },
{ name: 'a2', age: 12 },
],
},
{ name: 'b', age: 23 },
{
name: 'c',
subObjects: [
{ name: 'c1', age: 30 },
{ name: 'c2', age: 32 },
],
},
...
];
So, the array contains an array of objects, some objects also contain nested level object subObjects which contains the same structure as parent. Overall some 1st level object in array can have maximum two levels of nest (like above example shows).
Now, I need to have an array that gather all names of objects from above array, something like:
[
{ name: 'a' },
{ name: 'a1' },
{ name: 'a2' },
{ name: 'b' },
{ name: 'c' },
{ name: 'c1' },
{ name: 'c2' },
];
This is what I tried:
const arr1 = [
{
name: 'a',
subObjects: [
{ name: 'a1', age: 10 },
{ name: 'a2', age: 12 },
],
},
{ name: 'b', age: 23 },
{
name: 'c',
subObjects: [
{ name: 'c1', age: 30 },
{ name: 'c2', age: 32 },
],
},
];
const arr2 = arr1.map((obj) => {
return obj.subObjects ? obj.subObjects.flat() : obj.name;
});
console.log(arr2.flat());
But the output lost the 1st level object names for those who has nested objects. So, what is the best way to achieve what I need?
You could use a recursive flatMap to do it (with a little help from the spread oparator!):
const arr1 = [{name: 'a', subObjects:[{name: 'a1', age: 10}, {name: 'a2', age: 12},]}, {name: 'b', age: 23}, {name: 'c', subObjects:[{name: 'c1', age: 30}, {name: 'c2', age: 32},]}];
const recursiveFlat = (arr) => arr.flatMap(
a => a.subObjects
? [{name: a.name}, ...recursiveFlat(a.subObjects)]
: {name: a.name});
console.log(recursiveFlat(arr1));
This will work with any depth of nesting.

Accessing nested array in a json

let ages = data
.filter(isDog)
.map(dogYears)
.reduce(sum);
mL/hr
i want to find the best way of accessing array elements in a javascript object.
Eg: I want to find the first faculty name & first specializations for each course.
var students =
{
deptartment:[
{
name:'Computer Science',
age:20,
Course:[
{ id: 100000
name:'Object Oriented Programming',
faculty:[
{
id:123,
name:'John',
Specialization: [
{name: 'science'},
{name: 'Physics'}
]
}
]
},
{ id: 100001
name:'C#',
faculty:[
{
id:124,
name:'Denis',
Specialization: [
{name: 'Ecnonomics'},
{name: 'Physics'}
]
}
]
}
],
}
]
};
I know i can get the faculty name and specialization by
var courses= deptartment && deptartment.Course ;
var facultyWithSpecialization= {};
if(courses){
courses.forEach(course =>{
var fname = course.faculty && course.faculty[0].name;
var s= course.faculty && course.faculty.Specialization;
facultyWithSpecialization[fname] = s && s[0].name;
})
}
use Object.assign({}, deptartment.Course) instead of department.Course
tried to use the below code but it doesn't make much difference.
var courses=Object.values(Object.assign({}, deptartment.Course));
var fname = Object.values(Object.assign({}, course.faculty[0].Specialization[0]));
Expecting
'John': 'science'
'Denis': 'Ecnonomics'
You can try this. There were many error in the object including spelling mistakes and formatting
var students = {
deptartment: [{
name: 'Computer Science',
age: 20,
Course: [{
id: 100000,
name: 'Object Oriented Programming',
faculty: [{
id: 123,
name: 'John',
Specialization: [{
name: 'science'
},
{
name: 'Physics'
}
]
},
{
id: 124,
name: 'Denis',
Specialization: [{
name: 'Ecnonomics'
},
{
name: 'Physics'
}
]
}
]
}],
}]
}
var obj = {};
students.deptartment.forEach((e) => {
e.Course.forEach((k) => {
k.faculty.forEach((l) => {
obj[l.name] = l.Specialization[0].name
})
})
})
console.log(obj)
I think you meant department instead of deptartment.
I modified a bit your JSON as it was a bit buggy:
var students = {
departments:[
{
name:'Computer Science',
age:20,
Courses:[
{ id: 100000,
name:'Object Oriented Programming',
faculty:[
{
id:123,
name:'John',
Specialization: [
{name: 'science'},
{name: 'Physics'}
]
},
{
id:124,
name:'Denis',
Specialization: [
{name: 'Ecnonomics'},
{name: 'Physics'}
]
}
]
}
],
}]
}
You can use map to achieve this nesting:
students.departments.map(
department => department.Courses.map(
course => course.faculty.map(
student => ({
name: student.name,
specialization: student.Specialization[0].name // check nulls here!
})
)
)
)

Filtering array of objects by searching nested object properties

I have an array of objects that I want to filter by comparing a nested property to a search term.
For example:
var array = [
{category: 'Business'
users: [
{name: 'Sally'
tags: [{tag: 'accounting'}, {tag: 'marketing'},...]
},
{name: 'Bob'
tags: [{tag: 'sales'}, {tag: 'accounting'},...]
}...
]
},
{category: 'Heritage'
users: [
{name: 'Linda'
tags: [{tag: 'Italy'}, {tag: 'Macedonia'},...]
},
{name: 'George'
tags: [{tag: 'South Africa'}, {tag: 'Chile'},...]
},...
]
},...
[
Essentially I want to filter the base array of objects by a search terms that include characters from the tag property string in the nested objects 2 arrays down.
So a search for 'market' would result in
[
{category: 'Business'
users: [
{name: 'Sally'
tags: [{tag: 'accounting'}, {tag: 'marketing'},...]
},
{name: 'Bob'
tags: [{tag: 'sales'}, {tag: 'accounting'},...]
}...
]
}
]
Thank you.
You could use Array#filter with looking into the nested arrays by using Array#some.
If the tag is found in a nested array, then iteration stops and the result is given back to the filter callback.
var array = [{ category: 'Business', users: [{ name: 'Sally', tags: [{ tag: 'accounting' }, { tag: 'marketing' }] }, { name: 'Bob', tags: [{ tag: 'sales' }, { tag: 'accounting' }] }] }, { category: 'Heritage', users: [{ name: 'Linda', tags: [{ tag: 'Italy' }, { tag: 'Macedonia' }] }, { name: 'George', tags: [{ tag: 'South Africa' }, { tag: 'Chile' }] }] }],
tag = 'marketing',
result = array.filter(a => a.users.some(u => u.tags.some(t => t.tag.includes(tag))));
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
The solution using Array.prototype.some() function:
var arr = [{ category: 'Business', users: [{ name: 'Sally', tags: [{ tag: 'accounting' }, { tag: 'marketing' }] }, { name: 'Bob', tags: [{ tag: 'sales' }, { tag: 'accounting' }] }] }, { category: 'Heritage', users: [{ name: 'Linda', tags: [{ tag: 'Italy' }, { tag: 'Macedonia' }] }, { name: 'George', tags: [{ tag: 'South Africa' }, { tag: 'Chile' }] }] }],
search_key = 'market',
result = [];
arr.forEach(function(o){
if (o.users.some(function(v){
return v.tags.some(function(i){ return i.tag.indexOf(search_key) !== -1; });
})) {
result.push(o);
}
});
console.log(result);
Try this:
function search(term){
return
Array.filter(array,function(item){
return JSON.stringify(obj).indexOf(term)!=-1;
});
}
So :
console.log(search('market'));
I hope to be helpful for you:)
The concatAll and concatMap definitions are taken from http://reactivex.io/learnrx/
Array.prototype.concatAll = function() {
var results = [];
this.forEach(function(subArray) {
results.push.apply(results, subArray);
});
return results;
};
Array.prototype.concatMap = function(projectionFunctionThatReturnsArray) {
return this.
map(function(item) {
return projectionFunctionThatReturnsArray(item);
}).
// apply the concatAll function to flatten the two-dimensional array
concatAll();
};
function filterByTags(keyword) {
return array.filter(function (item) {
var allTags = item.users.concatMap(function (user) {
return user.tags.map(function (tag) {
return tag.tag;
});
});
return allTags.some(function (tag) {
return tag.indexOf(keyword) > -1;
});
});
}
console.log(filterByTags('market'));
Of course you could inline the allTags variable for more conciseness.
The filter applied to the initial array will return all items that have users whose tags contain the keyword supplied. The strategy is to build a flattened version of the users' tags and apply some on that.
You can use array.filter like this:
function getFiltered(val) {
return array.filter(category == val);
}
This function will return a new array instance, only with the category keys you passed as the val params.
Note: I am taking a shortcut-like approach to this, primarily to provide a different perspective to the problem.
Instead of deep-searching the properties and arrays under the main array, you can create a json string of the users property and search within that. So I have created a new property usersString that temporarily stores the JSON string of the value against users property.
item.usersString = JSON.stringify(item.users);
Now, this would not be a perfect implementation, but it would almost always work. Also, if you stored this property within the browser (without storing it back to the DB), and used it to quick-search for every time user searches, I think it would be more performant that deep-searching entire array.
var array = [{
category: 'Business',
users: [{
name: 'Sally',
tags: [{
tag: 'accounting'
}, {
tag: 'marketing'
}]
},
{
name: 'Bob',
tags: [{
tag: 'sales'
}, {
tag: 'accounting'
}]
}
]
},
{
category: 'Heritage',
users: [{
name: 'Linda',
tags: [{
tag: 'Italy'
}, {
tag: 'Macedonia'
}]
},
{
name: 'George',
tags: [{
tag: 'South Africa'
}, {
tag: 'Chile'
}]
}
]
}
];
var key = "market";
// Convert the users property into a string - so that it works as a quick search target.
array.forEach(function(item) {
item.usersString = JSON.stringify(item.users);
});
var filteredItems = array.filter(function(item) {
return item.usersString.toLowerCase().indexOf(key.toLowerCase()) >= 0;
});
// Delete the usersString property - if required.
filteredItems.forEach(function(item) {
item.usersString = undefined;
// Or,
// delete item.usersString;
})
console.log(filteredItems);

Categories