let bigArray = [
{
Name: 'Alice',
children: [
{Name: 'AliceChild1', Country: 'country1'},
{Name: 'AliceChild2', Country: 'country2'}
]
},
{
Name: 'Bob',
children: [
{Name: 'BobChild1', Country: 'country3'},
{Name: 'BobChild2', Country: 'country4'}
]
},
{
Name: 'Sam',
children: [
{Name: 'SamChild1', Country: 'country5'},
{Name: 'SamChild2', Country: 'country6'}
]
},
]
I want to remove an object from array inside another array. Property Name is unique. For an example if BobChild2 is removed bigArray should return as
let bigArray = [
{
Name: 'Alice',
children: [
{Name: 'AliceChild1', Country: 'country1'},
{Name: 'AliceChild2', Country: 'country2'}
]
},
{
Name: 'Bob',
children: [
{Name: 'BobChild1', Country: 'country3'},
]
},
{
Name: 'Sam',
children: [
{Name: 'SamChild1', Country: 'country5'},
{Name: 'SamChild2', Country: 'country6'}
]
},
]
What is the best way to do this in JavaScript ?
Updated:
My answer
function removeChild(bigArray, childName) {
let copyBigArray = []
bigArray.forEach((item) => {
let Obj = {
Name: item.Name,
children: item.children.filter(c => c.Name !== childName)
}
copyBigArray.push(Obj)
})
return copyBigArray
}
Try this way:
let bigArray = [{
Name: 'Alice',
children: [{
Name: 'AliceChild1',
Country: 'country1'
},
{
Name: 'AliceChild2',
Country: 'country2'
}
]
},
{
Name: 'Bob',
children: [{
Name: 'BobChild1',
Country: 'country3'
},
{
Name: 'BobChild2',
Country: 'country4'
}
]
}
]
bigArray.forEach(function(o) {
o.children = o.children.filter(s => s.Name != 'BobChild2');
});
console.log(bigArray);
To support any nested depth you can do something like this:
function searchAndRemove(arr, query) {
for (var i = arr.length; i > 0; i--) {
if (query == arr[i].Name) {
arr.splice(i, 1);
}
}
if (arr.children) {
searchAndRemove(arr.children, query);
}
}
searchAndRemove(bigArray, 'BobChild2');
This will go through your array recursively until it finds all occurrences of BobChild2 and removes them.
Well the structure isn't optimal because it'll require iterating over 2 arrays, but I'd use filter() (documentation) something like this:
function deepFilter(array, name) {
return array.map(arr => {
if (!arr || !arr.children) {
return arr;
}
arr.children = arr.children.filter(c => c.Name !== name);
return arr;
})
}
Filter has to return a Boolean to know if the element should be returned or not.
Map has to return an element.
If you want to remove an element from the first Array once its children are empty, you could replace the map by a filter.
function deepFilter(array, name) {
return array.filter(arr => {
if (!arr || !arr.children || !arr.children.length) {
return false;
}
arr.children = arr.children.filter(c => c.Name !== name);
return arr && arr.children && arr.children.length;
})
}
--
Use them by doing:
const new = deepFilter(bigArray, 'SamChild1')
Here is an example how you could achieve it:
let bigArray = [
{
Name: 'Alice',
children: [
{Name: 'AliceChild1', Country: 'country1'},
{Name: 'AliceChild2', Country: 'country2'}
]
},
{
Name: 'Bob',
children: [
{Name: 'BobChild1', Country: 'country3'},
{Name: 'BobChild2', Country: 'country4'}
]
},
{
Name: 'Sam',
children: [
{Name: 'SamChild1', Country: 'country5'},
{Name: 'SamChild2', Country: 'country6'}
]
},
]
function filterName(name, data) {
return data.reduce((arr, item) => {
if (item.Name != name) {
if (item.children) item.children = filterName(name, item.children)
arr.push(item)
}
return arr
}, [])
}
console.log(filterName("BobChild2", bigArray));
A main loop for the initial values of the array
Another loop for children values
The first parameter is the array itself that wants to be filtered, for example: bigArray
The second parameter is the value for the filter, for example: BobChild2
The third parameter is the key for the filter, for example: Name
let bigArray = [{
Name: 'Alice',
children: [
{ Name: 'AliceChild1', Country: 'country1' },
{ Name: 'AliceChild2', Country: 'country2' }
]
},
{
Name: 'Bob',
children: [
{ Name: 'BobChild1', Country: 'country3' },
{ Name: 'BobChild2', Country: 'country4' }
]
},
{
Name: 'Sam',
children: [
{ Name: 'SamChild1', Country: 'country5' },
{ Name: 'SamChild2', Country: 'country6' }
]
},
];
function filterBigArray(array, value, filter) {
let result = [];
bigArray.forEach(person => {
let childs = [];
person.children.forEach(children => {
if (children[filter] !== value) {
childs.push(children);
}
});
result.push(childs);
});
return result;
}
let res = filterArray(bigArray, 'BobChild2', 'Name');
console.log(res);
You can also filter different keys, for example:
let res = filterBigArray(bigArray, 'country3', 'Country');
console.log(res);
Related
I have the following array that I'd like to transform into an Object with unique hobbies as the keys
const arr = [
{ name: 'Joe', hobbies: ['skating', 'biking', 'music'] },
{ name: 'Kim', hobbies: ['fishing', 'biking', 'karate'] },
{ name: 'Ben', hobbies: ['surfing'] },
]
I use lodash's handy groupBy function but it groups the multiple array elements into single keys like so
{
'skating,biking,music': [
{ name: 'Joe' }
],
'fishing,biking,karate': [
{ name: 'Kim' }
],
'surfing': [
{ name: 'Ben' }
],
}
What I need is the following output (note the objects are repeated for each of their respective hobbies)
{
biking: [
{ name: 'Joe' },
{ name: 'Kim' }
],
skating: [
{ name: 'Joe' }
],
karate: [
{ name: 'Kim' }
],
surfing: [
{ name: 'Ben' }
],
...
}
Is there a simple way to group this array without looping through each array element, splitting them up and regrouping? Would like to avoid this if there's better utility method out there I'm unaware of
You can iterate each item and each hobbie and then add it to a result object:
const arr = [
{ name: 'Joe', hobbies: ['skating', 'biking', 'music'] },
{ name: 'Kim', hobbies: ['fishing', 'biking', 'karate'] },
{ name: 'Ben', hobbies: ['surfing'] }
]
const result = {};
arr.forEach(item =>
item.hobbies.forEach(hobbie =>
result[hobbie] = (result[hobbie] || []).concat({name: item.name})
)
)
console.log(result);
const arr = [
{ name: 'Joe', hobbies: ['skating', 'biking', 'music'] },
{ name: 'Kim', hobbies: ['fishing', 'biking', 'karate'] },
{ name: 'Ben', hobbies: ['surfing'] }
]
const result = {};
arr.forEach(item =>
item.hobbies.forEach(hobbie =>
result[hobbie] = result[hobbie]?[...result[hobbie],{name: item.name}]: [{name: item.name}]
)
)
console.log(result);
I've renamed arr to people for better understanding.
const people = [
{ name: 'Joe', hobbies: ['skating', 'biking', 'music'] },
{ name: 'Kim', hobbies: ['fishing', 'biking', 'karate'] },
{ name: 'Ben', hobbies: ['surfing'] },
];
function transform(people) {
// get all hobbies and remove duplicates
const hobbies = [... new Set(
people.reduce((hobbies, person) => hobbies.concat(person.hobbies), [])
)];
const res = {};
// take a hobby and use it as key
for (let hobby of hobbies) {
res[hobby] = people
.filter((person) => person.hobbies.includes(hobby))
.map((person) => { return { name: person.name }; });
}
return res;
}
console.log(transform(people));
I have been trying to retrieve inner elements using ReactJs. If my input is country=NZ then I am expecting 4 results, it should consider the inner array also, however when I used jsonQuery it is not able to go to the inner array and fetching only 3 results, and it's not going inside friends1. Is there any way we can fetch inner array elements as well?
var result1 = jsonQuery('grouped_people[**][*country=NZ]', {data: data1}).value;
console.log(result1);
console.log("Array1");
var data1 = {
grouped_people: {
'friends': [
{ name: 'Steve', country: 'NZ' },
{ name: 'Jane', country: 'US' },
{ name: 'Mike', country: 'AU' },
{ name: 'Mary', country: 'NZ' },
],
'enemies': [
{ name: 'Steve', country: 'NZ' },
{
'friends1': [
{ name: 'Evil Steve', country: 'India' },
{ name: 'Betty', country: 'NZ' },
]
}]
}
}
You should be able to optimise it as per your convenience.
var data1 = {
grouped_people: {
'friends': [
{ name: 'Steve', country: 'NZ' },
{ name: 'Jane', country: 'US' },
{ name: 'Mike', country: 'AU' },
{ name: 'Mary', country: 'NZ' },
],
'enemies': [
{ name: 'Steve', country: 'NZ' },
{
'friends1': [
{ name: 'Evil Steve', country: 'India' },
{ name: 'Betty', country: 'NZ' },
]
}]
}
}
const COUNTRY = "NZ";
const arr = [];
function getObject(obj) {
if (obj.country) {
if (obj.country === COUNTRY) {
arr.push(obj);
return;
}
} else {
Object.keys(obj).map(key => {
obj[key].map(item => {
getObject(item);
});
});
}
}
getObject(data1.grouped_people);
console.log(arr);
I used recursion to check (inside the check function ) whether the passed argument is an object. If so, then check for the key country and name and if they exist and the country value matches the inp then increment the result1 by 1 and if the keys don't exist, call the function on the values of the keys.
if the type is not object, then, get every element of the new object(which can only be an array) and call the function over it.
let inp = 'NZ';
var result1 = 0;
console.log(result1);
console.log("Array1");
var data1 = {
grouped_people: {
'friends': [
{ name: 'Steve', country: 'NZ' },
{ name: 'Jane', country: 'US' },
{ name: 'Mike', country: 'AU' },
{ name: 'Mary', country: 'NZ' },
],
'enemies': [
{ name: 'Steve', country: 'NZ' },
{
'friends1': [
{ name: 'Evil Steve', country: 'India' },
{ name: 'Betty', country: 'NZ' },
]
}]
}
}
function check(obj) {
if (!Array.isArray(obj)) {
let keys = Object.keys(obj);
if(keys.includes('country') && keys.includes('name')) {
if(obj['country'] == inp) {
result1 += 1
}
}else {
keys.map(key => {
check(obj[key])
})
}
}
else {
obj.map(ob => {
check(ob)
})
}
return result1;
}
let res = check(data1);
console.log(res);
If using jsonQuery is not absolutely necessary, you can spread out the objects that you want into a dataset and format it using filter.
let resultSet = [...data1.grouped_people.friends, data1.grouped_people.enemies[0], ...data1.grouped_people.enemies[1].friends1]
resultSet.filter(e => e.country == 'NZ')
I want to search an nested object by values of property 'name' and the result will keep its all parents.
For example,
const object = [
{
name: 'Mary',
children: [
{
name: 'Jack',
},
{
name: 'Kevin',
children: [
{
name: 'Lisa',
}
]
}
]
},
{
name: 'Gina',
children: [
{
name: 'Jack',
}
]
}
]
If I search 'Mary', it should be return:
[
{
name: 'Mary',
}
]
If I search 'Jack', it should be return:
[
{
name: 'Mary',
children: [
{
name: 'Jack',
}
]
},
{
name: 'Gina',
children: [
{
name: 'Jack',
}
]
}
]
If I search 'Lisa', it should be return:
[
{
name: 'Mary',
children: [
{
name: 'Jack',
children: [
{
name: 'Lisa',
}
]
}
]
}
]
I tried some methods but I could only filter two layer. As below:
return object.filter(data => {
if (data.children) {
return data.name.includes(keyword) || data.children.find(item => item.name.includes(keyword));
}
return data.name.includes(keyword);
})
Could someone point me in the right direction? Thanks!
You could build an object and if nested, check the children and create the parents, if necessary.
function getObjects(array, target) {
return array.reduce((r, { name, children = [] }) => {
if (name === target) {
r.push({ name });
return r;
}
children = getObjects(children, target);
if (children.length) {
r.push({ name, children })
}
return r;
}, []);
}
var data = [{ name: 'Mary', children: [{ name: 'Jack' }, { name: 'Kevin', children: [{ name: 'Lisa' }] }] }, { name: 'Gina', children: [{ name: 'Jack' }] }];
console.log(getObjects(data, 'Mary'));
console.log(getObjects(data, 'Jack'));
console.log(getObjects(data, 'Lisa'));
.as-console-wrapper { max-height: 100% !important; top: 0; }
Here is an example of a depth-first approach:
function searchWithParents(tree, query) {
let results = [];
for (const {name, children} of tree) {
if (name === query) {
results.push({name});
}
if (children) {
const subtreeResults = searchWithParents(children, query);
const mappedResults = subtreeResults.map(child => ({name, children: [child]}))
results = results.concat(mappedResults);
}
}
return results;
}
console.log(searchWithParents(object, 'Mary'));
console.log(searchWithParents(object, 'Jack'));
console.log(searchWithParents(object, 'Lisa'));
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!
})
)
)
)
I would like to create an array of all "department" from the "users" array without duplicate in ES6.
I've tried with forEach, reduce, filter, without success...
Users array:
let users = [{
firstname: 'test',
department: {
id: 1,
name: 'hello'
}
},
{
firstname: 'test2',
department: {
id: 2,
name: 'hello2'
}
},
{
firstname: 'test2',
department: {
id: 1,
name: 'hello'
}
}
]
Result expected:
// Expected
departments = [{
id: 1,
name: 'hello'
},
{
id: 2,
name: 'hello2'
}
] */
My own experiment:
let departments = []
users.forEach(user => {
console.log('-------------------')
console.log(departments)
console.log(user)
console.log(user.department)
console.log(departments.includes(user.department))
if (!departments.includes(user.department)) {
departments.push(user.department)
}
console.log(departments)
})
console.log(departments)
Thanks for your help!
Problem:
Your problem is that you are checking for departments with Array#includes() which is rather used with primitives such as Number and string and doesn't compare objects, try not to use it as it's not compatible with IE also.
Solution:
You can do it using Array#map() and Array#filter() methods:
var deps = users.map(u => u.department);
let results = deps.filter((item, pos) => {
return deps.map(v => v.id).indexOf(item.id) == pos;
});
First map the items to keep only the department object.
Then filter the departments to exclude the ones that has the same id.
Demo:
This is a working demo:
let users = [{
firstname: 'test',
department: {
id: 1,
name: 'hello'
}
},
{
firstname: 'test2',
department: {
id: 2,
name: 'hello2'
}
},
{
firstname: 'test2',
department: {
id: 1,
name: 'hello'
}
}
];
var deps = users.map(u => u.department);
let results = deps.filter((item, pos) => {
return deps.map(v => v.id).indexOf(item.id) == pos;
});
console.log(results);
Just map to the departments, then filter out based on the id:
const ids = new Set;
const result = users
.map(user => user.department)
.filter(({ id }) => !ids.has(id) && ids.add(id));
(This is O(n) as Set lookup / insertion is O(1))
You can use Array.reduce() for that:
let users = [{
firstname: 'test',
department: {
id: 1,
name: 'hello'
}
},
{
firstname: 'test2',
department: {
id: 2,
name: 'hello2'
}
},
{
firstname: 'test2',
department: {
id: 1,
name: 'hello'
}
}
];
let departments = users.reduce((acc, obj)=>{
let exist = acc.find(({id}) => id === obj.department.id);
if(!exist){
acc.push({id:obj.department.id, name: obj.department.name});
}
return acc;
}, []);
console.log(departments);