I am looking to turn this flat array into an nested array but it keeps returning as empty. Not sure how to solve this one or what I am missing but this is driving me crazy.
Flat array:
var names =[
{ name: 'b', parent: 'Brown' },
{ name: 'a', parent: 'Brown' },
{ name: 'h', parent: 'Green' },
{ name: 'c', parent: 'Green' },
];
Desired output of array:
[{
name: 'Brown',
children: [{
name: 'a',
children: []
},
{
name: 'b',
children: []
}
]
}, {
name: 'Green',
children: [{
name: 'h',
children: []
},
{
name: 'c',
children: []
}
]
}
}]
Js:
function getNestedChildren(arr, parent) {
var children = [];
for(var i =0; i < arr.length; ++i) {
if(arr[i].parent == parent) {
var grandChildren = getNestedChildren(arr, arr[i].name)
if(grandChildren.length) {
arr[i].children = grandChildren;
}
children.push( arr[i]);
}
}
return children;
}
var nest = getNestedChildren(names, names.parent);
console.log( nest);
var names =[
{ name: 'b', parent: 'Brown' },
{ name: 'a', parent: 'Brown' },
{ name: 'h', parent: 'Green' },
{ name: 'c', parent: 'Green' },
{ name: 'j', parent: 'Brown' }
];
function groupBy(arr, f) {
return arr.reduce((r, v, i, a, k = f(v)) => ((r[k] || (r[k] = [])).push(v), r), {});
}
function nestArray(array){
var newArray=[],
resultGrouped = groupBy(names, function(c) {
return c.parent
});
for (var key in resultGrouped){
var item=resultGrouped[key];
newArray.push({
name:key,
children:item.map(function(map){
delete map.parent;
map.children=[];
return map;
})
});
}
return newArray;
}
console.log(nestArray(names));
You can create a new object, for each item assign an array to a key with the parent name, and concatenate the item to that array
var names =[
{ name: 'b', parent: 'Brown' },
{ name: 'a', parent: 'Brown' },
{ name: 'h', parent: 'Green' },
{ name: 'c', parent: 'Green' },
];
const getGroup=(groups, parent) => {
let group = groups.find(g=>g.parent===parent);
if(!group){
group=({parent,children:[]});
groups.push(group);
}
return group;
}
let grouped = []
names.forEach(item=> getGroup(grouped,item.parent).children.push(item))
console.log(grouped)
So for starters, you need to loop through the names and send each parent to your getNestedChildren() function.
var names =[
{ name: 'b', parent: 'Brown' },
{ name: 'a', parent: 'Brown' },
{ name: 'h', parent: 'Green' },
{ name: 'c', parent: 'Green' },
];
var nest = [];
for (var i = 0; i < names.length; i++) {
nest.push(getNestedChildren(names, names[i].parent));
}
Also your getNestedChildren() is currently trying to modify and send back the old names array. I'd suggest creating a new object instead and send back the object with the children on it.
function getNestedChildren(arr, parent) {
var children = [];
var parentObj = {};
parentObj.name = parent;
for(var i =0; i < arr.length; ++i) {
if(arr[i].parent == parent) {
children.push(getNestedChildren(arr, arr[i].name));
}
}
parentObj.children = children;
return parentObj;
}
Related
The nested array looks like this:
var arr = [{
id: 2,
name: 'a',
children: []
}, {
id: 5,
name: 'b',
children: [{
id: 14,
name: 'b2'
}]
}, {
id: 15,
name: 'd',
children: []
}];
How can I make a list of ancestor elements, from any given element?
For example, if given element has id: 14 the list should return only the parent:
[{
id: 5,
name: 'b',
children: [...]
}]
I'm looking to replicate a "breadcrumb" navigation
You could handover an object which is the parent and search recursive for the wanted id.
function getParent(object, id) {
var result;
(object.children || []).some(o => result = o.id === id ? object : getParent(o, id));
return result;
}
var array = [{ id: 2, name: 'a', children: [] }, { id: 5, name: 'b', children: [{ id: 14, name: 'b2' }] }, { id: 15, name: 'd', children: [] }];
console.log(getParent({ children: array }, 14));
.as-console-wrapper { max-height: 100% !important; top: 0; }
If you like to hand over the array, you could take a nested approach with recursive function.
function getParent(children, id) {
function iter(object) {
var result;
(object.children || []).some(o => result = o.id === id ? object : iter(o));
return result;
}
return iter({ children });
}
var array = [{ id: 2, name: 'a', children: [] }, { id: 5, name: 'b', children: [{ id: 14, name: 'b2' }] }, { id: 15, name: 'd', children: [] }];
console.log(getParent(array, 14));
.as-console-wrapper { max-height: 100% !important; top: 0; }
If we can assume that only two levels exist (parents and children, not children of children) then the following function findAncestor() does what you need. It iterates over all parent elements and checks if they have a child with the relevant ID.
function findAncestor(id) {
for (let i = 0; i < arr.length; i++) {
let obj = arr[i];
if (obj.hasOwnProperty('children')) {
if (obj.children.length > 0) {
for (let j = 0; j < obj.children.length; j++) {
if (obj.children[j].id === id) {
return obj;
}
}
}
}
}
return null;
}
console.info(findAncestor(14));
If you need to handle the case that a child with same ID can occur in several parents, you should use a result array and add all found results to it before returning it in the end.
You can try this way,
var arr = [{
id: 2,
name: 'a',
children: []
}, {
id: 5,
name: 'b',
children: [{
id: 14,
name: 'b2'
}]
}, {
id: 15,
name: 'd',
children: []
}];
function getAncestor(obj,id,ancestor){
if(obj.id===id){
console.log(ancestor);
}
else
if(obj&& obj.children && obj.children.length)
obj.children.forEach(item=>this.getAncestor(item,id,obj));
}
arr.forEach(o=>getAncestor(o,14,{}));
The Depth First Search Algorithm - get parent node is:
function getParentNodeByKey(obj, targetId, paramKey) {
if (obj.children) {
if (obj.children.some(ch => ch[paramKey] === targetId))
return obj;
else {
for (let item of obj.children) {
let check = this.getParentNodeByKey(item, targetId, paramKey)
if (check) {
return check;
}
}
}
}
return null
}
and code to test:
var arr = [{
id: 2,
name: 'a',
children: []
}, {
id: 5,
name: 'b',
children: [{
id: 14,
name: 'b2'
}]
}, {
id: 15,
name: 'd',
children: []
}];
let parentElement;
const valueToFind = 14;
const desiredkey = 'id';
for (let i = 0; i < arr.length; i++) {
parentElement = getParentNodeByKey(arr[i], valueToFind, desiredkey);
if(parentElement)
break;
}
console.log(`The parent element is:`, parentElement);
Here is my simple array of objects
const array = [
{ name: 'a', val: '1234' },
{ name: 'b', val: '5678' },
{ name: 'c', val: '91011' },
{ name: 'c', val: '123536' },
{ name: 'e', val: '5248478' },
{ name: 'c', val: '5455' },
{ name: 'a', val: '548566' },
{ name: 'a', val: '54555' }
]
I need to group consecutive name elements and push the corresponding val. So the expected output should be
const array = [
{ name: 'a', vals: '1234' },
{ name: 'b', vals: '5678' },
{ name: 'c', vals: ['91011', '123536'] },
{ name: 'e', vals: '5248478' },
{ name: 'c', vals: '5455' },
{ name: 'a', vals: ['548566', '54555'] }
]
I tried it But could not get over it. Please help
const output = []
const result = array.reduce((a, c) => {
if (a.name === c.name) {
output.push(a);
}
}, []);
You were actually quite close:
const output = [];
array.reduce((a, c) => {
if (a.name === c.name) { // current element equals previous element, lets merge
a.values.push(c.val);
} else output.push(a = { name: c.name, values: [c.val] ); // otherwise add new entry
return a; // the current element is the next previous
} , {}); // start with an empty a, so that c always gets pushed
Note that it makes little sense to store numbers as string though.
You can reduce the array like this. Compare the current name with previous item's name. If they are not the same, add a new item to the accumulator. If they are the same, then use concat the merge val with the last item in accumulator. concat is used because vals could either be a string or an array.
const array = [
{ name: 'a', val: '1234' },
{ name: 'b', val: '5678' },
{ name: 'c', val: '91011' },
{ name: 'c', val: '123536' },
{ name: 'e', val: '5248478' },
{ name: 'c', val: '5455' },
{ name: 'a', val: '548566' },
{ name: 'a', val: '54555' }
]
const merged = array.reduce((acc, { name, val }, i, arr) => {
// check if name is same as the previous name
if (arr[i - 1] && arr[i - 1].name === name) {
const prev = acc[acc.length - 1]; // last item in the accumulator
prev.vals = [].concat(prev.vals, val)
} else
acc.push({ name, vals: val })
return acc
}, [])
console.log(merged)
const r = ['a', 'b', 'c', 'l', 'p'];
const arr = [{
name: "ss3",
id: 'c'
}, {
name: "ss2",
id: 'b'
}, {
name: "ss4",
id: 'p'
}, {
name: "ss1",
id: 'a'
}]
var newArray =arr.map((i)=>{
let e = r[i];
if(i.id===e){
return i
}
})
console.log(newArray)
Expected output
const arr = [{
name: "ss1",
id: 'a'
}, {
name: "ss2",
id: 'b'
}, {
name: "ss3",
id: 'c'
}, {
name: "ss4",
id: 'p'
}
]
Given two arrays r and arr, I wish to sort arr with respect to r, i.e. in alphabetical order by id.
https://jsbin.com/yitijiboso/edit?html,js,output
I think this might be a concise (although not very performant) way to achieve the desired output:
const arr1 = ['a', 'b', 'c', 'l', 'p'];
const arr2 = [
{
name: "ss3",
id: 'c'
},
{
name: "ss2",
id: 'b'
}, {
name: "ss4",
id: 'p'
},
{
name: "ss1",
id: 'a'
}
];
arr2.sort((a, b) => arr1.indexOf(a.id) - arr1.indexOf(b.id));
console.log(arr2);
Easy:
make a map from main 'arr' keyBy 'id' https://www.npmjs.com/package/lodash.keyby
loop across 'r', if key exist in new map, get value and push to new array
const arrMap = _.keyBy(arr, 'id');
let newR = [];
r.forEach( key => {
if ( arrMap[key] ) {
newR.push( arrMap[key] );
}
} );
console.log( 'new array', newR );
Taking a clue from #Petr Broz, here's my suggestion:
const r = ['a', 'b', 'c', 'l', 'p'];
const arr = [
{
name: "ss3",
id: 'c'
},
{
name: "ss2",
id: 'b'
}, {
name: "ss4",
id: 'p'
},
{
name: "ss1",
id: 'a'
}
];
arr.sort((a, b) => r.indexOf(a.id) > r.indexOf(b.id));
console.log(arr);
Main difference is that this code utilizes the arrays as named by the OP and uses a greater than comparison operator. However, if you just want to have the array arr sorted in alphabetical order you don't need to compare it with array r:
const arr = [
{
name: "ss3",
id: 'c'
},
{
name: "ss2",
id: 'b'
}, {
name: "ss4",
id: 'p'
},
{
name: "ss1",
id: 'a'
}
];
arr.sort(function(a, b)
{
if (a.id > b.id) {
return 1;
}
else
if (a.id < b.id) {
return -1;
}
else
{
return 0;
}
});
console.log(arr);
Note, in this example the return values are numeric instead of boolean values which would be helpful if the array to be sorted were to have duplicate values.
I have a multidimensional nested parent/child array as follows:
{
parent: {
name: "foo"
},
children: [
{
parent: {
name: "asdf",
},
children: []
},
{
parent: {},
children: []
},
{
parent: {},
children: []
},
...
]
}
What I want to do is apply a recursive function on this array, to flatten only the parent property and keep children as-is, in order to get something like this:
{
name: "foo",
children: [
{
name: "asdf",
children: []
},
{
children: []
},
{
children: []
},
...
]
}
I tried using underscore.js but I am unable to find a recursive function that could do the trick. Any thoughts?
You could use an iterative and recursive approach by iterating the array and updating the wanted properties.
If a children is found, iterate again.
var array = [{ parent: { name: 'a', surname: 'b', }, children: [{ parent: { name: 'c', surname: 'd', }, children: [{ parent: { name: 'e', surname: 'f' } }], childrenName: 'v', childrenSurname: 'w' }], childrenName: 'x', childrenSurname: 'y' }];
array.forEach(function iter(object) {
if (object.parent) {
object.name = object.parent.name;
object.surname = object.parent.surname;
delete object.parent;
}
object.children && object.children.forEach(iter);
});
console.log(array);
.as-console-wrapper { max-height: 100% !important; top: 0; }
hope this recurse function is what you need
const recurse = (data) => {
for(let i = 0, l = data.length; i < l; i++){
if(data[i]['parent']){
data[i]['name'] = data[i]['parent']['name']
delete data[i]['parent']
}
if(data[i]['children']) recurse(data[i]['children'])
}
}
I try to write insert into a tree data structure recursively in javascript with tree-node, but don't get it working.
So my question would be, how to approach the issue.
this is my data:
[ { id: 'a', children: [ 'b', 'c' ] },
{ id: 'b', children: [ '' ] },
{ id: 'c', children: [ 'b', 'd' ] },
{ id: 'd', children: [ 'b' ] } ]
I want that showing up in a tree like the following:
a
/\
b c
/\
b d
\
b
Edit: Added code
I Thought i could do something like this, but that doesn't work... and of course has high complexity because of the nested forEach:
var Node = require("tree-node");
var testarray =
[
{ id: 'a', children: [ 'b', 'c' ] },
{ id: 'b', children: [ '' ] },
{ id: 'c', children: [ 'b', 'd' ] },
{ id: 'd', children: [ 'b' ] }
]
function appendChildRecursive(parent) {
var childnode = new Node()
var data = parent.data("children")
testarray.forEach(function(item){
if(data !== undefined) {
data.forEach(function (child) {
if (item.id == child) {
childnode.data("id", child).data("children", item.children)
childnode = appendChildRecursive(childnode)
parent.appendChild(childnode)
}
})
}
})
return parent
}
var root = new Node();
root.data("id",testarray[0].id).data("children",testarray[0].children)
root=appendChildRecursive(root)
You could use a hash table for the last inserted nodes and keep the reference to the last nodes by overwriting the reference.
var data = [{ id: 'a', children: ['b', 'c'] }, { id: 'b', children: [] }, { id: 'c', children: ['b', 'd'] }, { id: 'd', children: ['b'] }],
tree = function (array) {
var nodes = Object.create(null),
r = {};
array.forEach(function (a) {
if (!nodes[a.id]) {
nodes[a.id] = { id: a.id, children: [] };
r = nodes[a.id];
}
a.children.forEach(function (b) {
nodes[b] = { id: b, children: [] };
nodes[a.id].children.push(nodes[b]);
});
});
return r;
}(data);
console.log(tree);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Your data structure is wrong.
Each 'leaf' should contain reference to the 'left' and 'right' element.
for example:
const first = { id: 'a', left: null, right: null };
const second = { id: 'b', left: null, right: first };
// etc...
The children approach would be more suitable for graph.
But you still have to store references, not ids.