I have 2 arrays of objects
The first array
[
{
value: 'Node 1',
id: 1,
childs: [
{
value: 'Node 2',
id : 2,
childs: [
{
value: 'Node 3',
id: 3
},
{
value: 'Node 4',
id: 4
}
]
}
]
}
]
and the second array
[
{
value: 'Node 1',
id: 1,
childs: [
{
value: 'Node 5',
id : 5
}
]
}
]
and i don't understand how can i concat these arrays of object in a tree structure.
I need this result
[
{
value: 'Node 1',
id: 1,
childs: [
{
value: 'Node 2',
id : 2,
childs: [
{
value: 'Node 3',
id: 3
},
{
value: 'Node 4',
id: 4
}
]
},
{
value: 'Node 5',
id : 5
}
]
}
]
and these arrays can be more difficult and have more childs.
How can i get what i want?
Try this Array#forEach function and Array#filter
var arr = [ { value: 'Node 1', id: 1, childs: [ { value: 'Node 2', id : 2, childs: [ { value: 'Node 3', id: 3 }, { value: 'Node 4', id: 4
} ] } ] } ];
var arr2= [ { value: 'Node 1', id: 1, childs: [ { value: 'Node 5', id : 5 } ] } ]
arr.forEach(function(a){
var k =arr2.filter(i=> a.value == i.value);
a.childs.push(...k[0].childs)
})
console.log(arr)
You could use an iterative and recursive approach by hashing the id for the actual level.
function update(target, source) {
var hash = Object.create(null);
target.forEach(function (o) {
hash[o.id] = o;
});
source.forEach(function (o, i, a) {
if (hash[o.id]) {
o.children && update(hash[o.id].children = hash[o.id].children || [], o.children)
} else {
target.push(o);
}
});
}
var array1 = [{ value: 'Node 1', id: 1, children: [{ value: 'Node 2', id: 2, children: [{ value: 'Node 3', id: 3 }, { value: 'Node 4', id: 4 }] }] }],
array2 = [{ value: 'Node 1', id: 1, children: [{ value: 'Node 5', id: 5 }] }];
update(array1, array2);
console.log(array1);
.as-console-wrapper { max-height: 100% !important; top: 0; }
You're looking for the concat() method. See this.
So your code should look like this:
array1[0].children.concat(array2[0].children);
Related
My Array of objects looks like this:
const categories = [{
id: 1,
name: 'level 1'
}, {
id: 2,
name: 'level 2'
}, {
id: 3,
name: 'level 3'
}];
I want to dynamically iterate over this categories array and output the name property as a string, separated with commas except the first object in the array
const output = 'level 2, level 3';
categories could potentially contain several objects.
const categories = [{
id: 1,
name: 'level 1'
}, {
id: 2,
name: 'level 2'
}, {
id: 3,
name: 'level 3'
}, ..., ..., ...];
however, it is important that the first object is not outputted
const categories = [{id: 1, name: 'level 1'}];
const output = '';
This is quite trivial
const getNames = arr => arr.map(item => item.name).slice(1).join(", "); // or slice first, map later
const categories1 = [{
id: 1,
name: 'level 1'
}, {
id: 2,
name: 'level 2'
}, {
id: 3,
name: 'level 3'
}];
const categories2 = [{
id: 1,
name: 'level 1'
}];
console.log("1:", getNames(categories1))
console.log("2:", getNames(categories2))
You could exclude first element by combining the use of destructing assignment and spread operator
const categories = [
{ id: 1, name: 'level 1' },
{ id: 2, name: 'level 2' },
{ id: 3, name: 'level 3' }
]
const [first, ...rest] = categories
const res = rest.map(cat => cat.name).join(', ')
console.log(res)
const categories = [{
id: 1,
name: 'level 1'
}, {
id: 2,
name: 'level 2'
}, {
id: 3,
name: 'level 3'
}];
const output = categories.reduce((ans, v, i) => {
return i === 1 ? v.name : (ans + ',' + v.name)
});
console.log(output)
This can be done in several ways. However, using Array.prototype.map() combined with Array.prototype.join() and Array.prototype.slice() is the easiest way.
const categories = [
{ id: 1, name: 'level 1' },
{ id: 2, name: 'level 2' },
{ id: 3, name: 'level 3' }
];
categories.slice(1).map(category => category.name).join(', ');
P.S.
using slice at first will make a new array starting from index 1 (second object)
using map will fill the new array with the value of "name" properties of all the objects in the array.
Finally, using join will convert the array to a string that consists of all the values in the array separated with the value you provide between the parentheses.
So I have the following Array. I can push to the parent level and child level however I can't seem to push to Children of Children or deeper. Here's the Array:
TREE_DATA: SegmentCategory[] = [
{
id: 1,
name: 'Category 1',
children: [
{
id: 2,
name: 'Category 1-1',
children: [
{
id: 4,
name: 'Category 1-1-a',
children: []
},
{
id: 5,
name: 'Category 1-1-b',
children: []
},
]
},
{
id: 6,
name: 'Category 1-2',
children: [
{
id: 7,
name: 'Category 1-2-a',
children: [
{
id: 8,
name: 'Category 1-2-1',
children: [
{
id: 9,
name: 'Category 1-2-2-a',
children: []
}
]
}
]
}
]
}
]
},
{
id: 10,
name: 'Category 2',
children: []
}
];
And here is the method I am using to add data to the array:
createCategory() {
this.id++;
if (this.formGroup.controls['parentCategory'].value == null) {
this.TREE_DATA.push(
{
id: this.id,
name: this.formGroup.controls['categoryName'].value,
children: []
});
} else {
this.TREE_DATA.forEach((v, index) => {
if (v.id === this.formGroup.controls['parentCategory'].value.id) {
this.TREE_DATA[index].children.push(
{
id: this.id,
name: this.formGroup.controls['categoryName'].value,
children: []
}
);
}
});
}
}
I know that I can just add an additional foreach however this doesn't seem like the best way to handle this, as I could want to go 5-6 layers down at any time to add a new nested child. What would be the best way to handle this?
You could take a recursive function for finding an object in the tree.
function find(array, id) {
var result;
array.some(object => {
if (object.id === id) return result = object;
return result = find(object.children || [], id);
});
return result;
}
var data = [{ id: 1, name: 'Category 1', children: [{ id: 2, name: 'Category 1-1', children: [{ id: 4, name: 'Category 1-1-a', children: [] }, { id: 5, name: 'Category 1-1-b', children: [] }] }, { id: 6, name: 'Category 1-2', children: [{ id: 7, name: 'Category 1-2-a', children: [{ id: 8, name: 'Category 1-2-1', children: [{ id: 9, name: 'Category 1-2-2-a', children: [] }] }] }] }] }, { id: 10, name: 'Category 2', children: [] }];
console.log(find(data, 9));
console.log(find(data, 'unknown'));
I have this array of objects and I want to get all the controls from this to another array:
this.formModel = {
sections: [
{
title: 'Section 01',
controls: [
new FormControlInput({
key: 'name 01',
label: 'Name 01'
}),
new FormControlSelect({
key: 'abc',
label: 'Abc'
})
]
},
{
title: 'Section 02',
controls: [
new FormControlInput({
key: 'name 02',
label: 'Name 02'
})
]
}
]
};
I am using map for this but I am not getting single array, I am getting array of arrays:
this.formModel.sections.map(function (x) { return x.controls; })
Getting this:
[
{
[{
key: 'name 01',
label: 'Name 01'
},
{
key: 'abc',
label: 'Abc'
}]
},
{
[{
key: 'name 02',
label: 'Name 02'
}]
}
]
What I want is this:
[
{
key: 'name 01',
label: 'Name 01'
},
{
key: 'abc',
label: 'Abc'
},
{
key: 'name 02',
label: 'Name 02'
}
]
You just need to flatten your array after mapping:
var obj = {
sections: [{
title: 'Section 01',
controls: [
{ key: 'name 01', label: 'Name 01' },
{ key: 'abc', label: 'Abc' }
]
}, {
title: 'Section 02',
controls: [
{ key: 'name 02', label: 'Name 02' }
]
}
]
};
var mapped = obj.sections.map(function (x) { return x.controls; });
var flattened = [].concat.apply([], mapped);
console.log(flattened);
To simplify your example:
// This is your structure:
var sections= [{
controls: [{}, {}] // C1
}, {
controls: [{}] // C2
}
];
// With the map, grabbing each `controls` property, and using that as an entry in your array:
var mapped = sections.map(function (x) { return x.controls; });
console.log(mapped);
// [[{},{}],[{}]]
// ^ C1 ^ C2
// We need to remove that extra layer of arrays:
var flattened = [].concat.apply([], mapped);
console.log(flattened);
You can use reduce to flatten the hierarchy
formModel.sections
.map(x => x.controls)
.reduce((prev, current) => prev.concat(current), [])
Use reduce instead of map:
let formModel = {
sections: [
{
title: 'Section 01',
controls: [
{
key: 'name 01',
label: 'Name 01'
},
{
key: 'abc',
label: 'Abc'
}
]
},
{
title: 'Section 02',
controls: [
{
key: 'name 02',
label: 'Name 02'
}
]
}
]
};
let result = formModel.sections.reduce((res, section) => {
return res = res.concat(section.controls);
}, []);
console.log(result);
I have two separate arrays of objects that I need to merge based if a specific key value matches. Might make more sense after analyzing the data:
Array 1
let categories = [
{ id: 5, slug: 'category-5', items: [] },
{ id: 4, slug: 'category-4', items: [] },
{ id: 3, slug: 'category-3', items: [] },
]
Array 2
let items = [
{ id: 5, data: [{ title: 'item title', description: 'item description' }] },
{ id: 5, data: [{ title: 'item title 2', description: 'item description 2' }] },
{ id: 4, data: [{ title: 'item title 4', description: 'item description 4' }] },
]
Expected Output
let mergedOutput = [
{ id: 5, slug: 'category-5',
items: [
{ title: 'item title', description: 'item description' },
{ title: 'item title 2', description: 'item description 2' }
]
},
{ id: 4, slug: 'category-4',
items: [
{ title: 'item title 4', description: 'item description 4' },
]
},
{ id: 3, slug: 'category-3', items: [] },
]
So....I need to add Array 2 to Array 1 if their id's match.
Array 1 will stay the same, but if Array 2 matches, the items property of Array 1 (empty) will be replaced by the data property of Array 2
I know this is a pretty basic / and redundant question, but I can't find the resources for my use case / object structure.
I was able to easily group arrays with lodash -- so if there is a similar solution with that library -- that would good! Or just some direction would suffice.
Thanks in advance!
You can loop first array and then use filter to get objects with same id as current element and add that items to current object.
let categories = [
{ id: 5, slug: 'category-5', items: [] },
{ id: 4, slug: 'category-4', items: [] },
{ id: 3, slug: 'category-3', items: [] },
]
let items = [
{ id: 5, data: [{ title: 'item title', description: 'item description' }] },
{ id: 5, data: [{ title: 'item title 2', description: 'item description 2' }] },
{ id: 4, data: [{ title: 'item title 4', description: 'item description 4' }] },
]
categories.forEach(function(e) {
var i = items.filter(a => a.id == e.id).map(a => a.data);
e.items = i;
})
console.log(categories)
You could reduce the items into categories:
let res = items.reduce((a, b) => {
let it = a.find(e => e.id === b.id);
if (! it) return a;
it.items = it.items.concat(b.data);
return a;
}, categories);
let categories = [{
id: 5,
slug: 'category-5',
items: []
},
{
id: 4,
slug: 'category-4',
items: []
},
{
id: 3,
slug: 'category-3',
items: []
},
];
let items = [{
id: 5,
data: [{
title: 'item title',
description: 'item description'
}]
},
{
id: 5,
data: [{
title: 'item title 2',
description: 'item description 2'
}]
},
{
id: 4,
data: [{
title: 'item title 4',
description: 'item description 4'
}]
},
];
let res = items.reduce((a, b) => {
let it = a.find(e => e.id === b.id);
if (! it) return a;
it.items = it.items.concat(b.data);
return a;
}, categories);
console.log(res);
It might be faster to get the ids in an object first, so we don't have to use find on the same id many times:
function merge(result, toMerge, mergeInto) {
let i = 0, hm = {};
for (let {id} of categories) {
hm[id] = i;
i++;
}
return toMerge.reduce((a,b) => {
let it = a[hm[b.id]];
if (!it) return a;
it[mergeInto] = it[mergeInto].concat(b.data);
return a;
}, result);
}
let categories = [
{ id: 5, slug: 'category-5', items: [] },
{ id: 4, slug: 'category-4', items: [] },
{ id: 3, slug: 'category-3', items: [] },
];
let items = [
{ id: 5, data: [{ title: 'item title', description: 'item description' }] },
{ id: 5, data: [{ title: 'item title 2', description: 'item description 2' }] },
{ id: 4, data: [{ title: 'item title 4', description: 'item description 4' }] },
];
function merge(result, toMerge, mergeInto) {
let i = 0, hm = {};
for (let {id} of categories) {
hm[id] = i;
i++;
}
return toMerge.reduce((a,b) => {
let it = result[hm[b.id]];
if (!it) return a;
it[mergeInto] = it[mergeInto].concat(b.data);
return a;
}, result);
}
console.log(merge(categories, items, 'items'));
I would make the categories as hash map and the key would be the id and iterate over all the items only.
then you get O(N) solution.
I was wondering if anyone could help figure out how i can set the value of a property in an object which can be found in a deep Array.
Below is an example of the Tree array
I would like to know how i can insert
var newObjectToInsert = {id: 999, name: 'new name'};
in the 'nodes' array of the object whose id === 3901
var tree = [
{
id: 1,
name: 'Level 1 - A',
nodes: [
{
id: 33,
name: 'Level 2 = A',
nodes: []
},
{
id: 21,
name: 'Level 2 = B',
nodes: []
}
]
},
{
id: 2,
name: 'Level 1 - B',
nodes: []
},
{
id: 3,
name: 'Level 1 - B',
nodes: [
{
id: 65,
name: 'Level 2 = A',
nodes: []
},
{
id: 124,
name: 'Level 2 = A',
nodes: [
{
id: 3901,
name: 'Level 3 - A'
},
{
id: 29182,
name: 'Level 3 - B',
nodes: [
{
id: 32423413,
name: 'Level 4 - A'
}
]
}
]
},
{
id: 534,
name: 'Level 2 = A',
nodes: []
}
]
},
];
You can use a native Array#some to achieve a recursive traversal. An advantage towards using this approach is it already provides a mechanism to stop the traversal once it finds the node that we want to insert the new object.
var inserted = tree.some(function cb(v) {
var nodes = v.nodes || [];
return v.id === nodeId?
(v.nodes = nodes).push(newObjectToInsert):
nodes.some(cb);
});
var tree = [{
id: 1,
name: 'Level 1 - A',
nodes: [{
id: 33,
name: 'Level 2 = A',
nodes: []
},
{
id: 21,
name: 'Level 2 = B',
nodes: []
}
]
},
{
id: 2,
name: 'Level 1 - B',
nodes: []
},
{
id: 3,
name: 'Level 1 - B',
nodes: [{
id: 65,
name: 'Level 2 = A',
nodes: []
},
{
id: 124,
name: 'Level 2 = A',
nodes: [{
id: 3901,
name: 'Level 3 - A'
},
{
id: 29182,
name: 'Level 3 - B',
nodes: [{
id: 32423413,
name: 'Level 4 - A'
}]
}
]
},
{
id: 534,
name: 'Level 2 = A',
nodes: []
}
]
},
];
var newObjectToInsert = {id: 999, name: 'new name'};
var nodeId = 3901;
var inserted = tree.some(function cb(v) {
var nodes = v.nodes || [];
return v.id === nodeId?
(v.nodes = nodes).push(newObjectToInsert):
nodes.some(cb);
});
console.log(tree);
body > div { min-height: 100%; top: 0; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.js"></script>
You have a recursive structure. So you visit all items you need a visitor.
Pseudo code:
function visit(visitor, tree) {
visitor(tree);
items.nodes.forEach(subTree => visit(visitor, subTree));
}
And use
visit(node => {
if (node.id === 'whatever'){
node.push({yournode});
}
}, tree);