I have the following data stored in a variable:
let categories = [
{
name: "a",
nodes: [
{
name: "aa",
nodes: [
{
name: "aaa"
}
]
},
{
name: "ab",
},
{
name: "ac",
},
{
name: "ad",
}
]
},
{
name: "b",
nodes: [
{
name: "ba",
},
{
name: "bb",
},
{
name: "bc",
},
{
name: "bd",
}
]
}
];
And I have the following recursive function which accepts the categories variable and name.
function getCategoryParents(categories, name) {
for (let index = 0; index < categories.length; index++) {
const category = categories[index];
if (category.name === name) {
}
if (category.nodes && category.nodes.length) {
category.nodes.forEach(cat => this.getCategoryParents([cat], name));
}
}
}
I want to return an array of names containing the name in the parameter and the parents of that name.
For example if I called getCategoryParents(categories, "aaa") it should returns ["a", "aa", "aaa"]. because aa is the parent of aaa and a is the parent of aa.
I hope it's clear.
I tweaked your function so it would actually return some values when it finds the matches :
function getCategoryParents(arr, name) {
for (let child of arr) {
if (child.name === name) {
return name;
} else if (child.nodes.length > 0) {
var x = getCategoryParents(child.nodes, name);
if (x) return Array.isArray(x) ? [child.name, ...x] : [child.name, x];
}
}
}
let categories = [
{
name: "a",
nodes: [
{
name: "aa",
nodes: [
{
name: "aaa"
}
]
},
{
name: "ab"
},
{
name: "ac"
},
{
name: "ad"
}
]
},
{
name: "b",
nodes: [
{
name: "ba"
},
{
name: "bb"
},
{
name: "bc"
},
{
name: "bd"
}
]
}
];
const result = getCategoryParents(categories, "aaa");
console.log(result); // ["a", "aa", "aaa"]
Related
I try to add an object to an array on the correct position by timestamp, but the first object is always the last in the list after sorting (which makes sense if you look at the sorting algorithm).
This is the array I want to sort
const arr = [
{ tstmp: "2022-12-13T10:36:35.164165+0100", name: "c" },
{ tstmp: "2022-12-13T10:18:33.798261+0100", name: "b" },
{ tstmp: "2022-12-14T12:54:50.109871+0100", name: "a" },
{ tstmp: "2022-12-13T10:18:36.161500+0100", name: "d" },
{ tstmp: "2022-12-13T10:36:30.584337+0100", name: "e" },
{ tstmp: "2022-12-14T13:07:23.681388+0100", name: "f" },
{ tstmp: "2022-12-14T16:07:23.681388+0100", name: "g" },
];
This is the sorting function
const insertItem = (newItem) => {
if (end.length === 0) {
end.push(newItem);
return;
}
const x = end.findIndex((x) => new Date(x.tstmp) > new Date(newItem.tstmp));
end.splice(x, 0, newItem);
return;
};
This is how I use the function
for (let i = 0; i < arr.length; i++) {
const item = arr[i];
insertItem(item);
}
console.log(end);
This is the Output
{ tstmp: '2022-12-13T10:18:33.798261+0100', name: 'b' },
{ tstmp: '2022-12-13T10:18:36.161500+0100', name: 'd' },
{ tstmp: '2022-12-13T10:36:30.584337+0100', name: 'e' },
{ tstmp: '2022-12-14T12:54:50.109871+0100', name: 'a' },
{ tstmp: '2022-12-14T13:07:23.681388+0100', name: 'f' },
{ tstmp: '2022-12-14T16:07:23.681388+0100', name: 'g' },
{ tstmp: '2022-12-13T10:36:35.164165+0100', name: 'c' },
The output is perfect besides the "c"
I believe that (unless you have many many items in the array) it may be more convenient to simply sort the array once all the items have been pushed to it:
const arr = [
{ tstmp: "2022-12-13T10:36:35.164165+0100", name: "c" },
{ tstmp: "2022-12-13T10:18:33.798261+0100", name: "b" },
{ tstmp: "2022-12-14T12:54:50.109871+0100", name: "a" },
{ tstmp: "2022-12-13T10:18:36.161500+0100", name: "d" },
{ tstmp: "2022-12-13T10:36:30.584337+0100", name: "e" },
{ tstmp: "2022-12-14T13:07:23.681388+0100", name: "f" },
{ tstmp: "2022-12-14T16:07:23.681388+0100", name: "g" },
].sort((a, b) => new Date(a.tstmp) - new Date(b.tstmp))
console.log(arr)
I have an Json Raw Data with Parent child relationship.I would like to generate Hierarchical json from flat with parent ID.I have tried below code its working fine but I would like to know how to remove children[] object if there is no data in children object.
var arry = [
{ Id: "1", Name: "abc", Parent: "", attr: "abc" },
{ Id: "2", Name: "abc", Parent: "1", attr: "abc" },
{ Id: "3", Name: "abc", Parent: "2", attr: "abc" },
{ Id: "4", Name: "abc", Parent: "2", attr: "abc" },
];
function convert(array) {
var map = {};
for (var i = 0; i < array.length; i++) {
var obj = array[i];
obj.children = [];
map[obj.Id] = obj;
var parent = obj.Parent || "-";
if (!map[parent]) {
map[parent] = {
children: [],
};
}
map[parent].children.push(obj);
}
return map["-"].children;
}
var r = convert(arry);
console.log("array", r);
console.log("result", JSON.stringify(r));
I have the feeling that there might be a better way, but how about just traverse through the map properties and delete the ones with an empty children array?
var arry = [
{ Id: "1", Name: "abc", Parent: "", attr: "abc" },
{ Id: "2", Name: "abc", Parent: "1", attr: "abc" },
{ Id: "3", Name: "abc", Parent: "2", attr: "abc" },
{ Id: "4", Name: "abc", Parent: "2", attr: "abc" },
];
function convert(array) {
var map = {};
for (var i = 0; i < array.length; i++) {
var obj = array[i];
obj.children = [];
map[obj.Id] = obj;
var parent = obj.Parent || "-";
if (!map[parent]) {
map[parent] = {
children: [],
};
}
map[parent].children.push(obj);
}
for (let prop in map) {
if (map[prop].children.length === 0) {
delete map[prop].children;
}
}
return map["-"].children;
}
var r = convert(arry);
console.log("array", r);
console.log("result", JSON.stringify(r));
Using map-reduce can be done very easily in O(n)
const source = [
{ Id: "1", Name: "abc", Parent: "", attr: "abc" },
{ Id: "2", Name: "abc", Parent: "1", attr: "abc" },
{ Id: "3", Name: "abc", Parent: "2", attr: "abc" },
{ Id: "4", Name: "abc", Parent: "2", attr: "abc" },
];
function tree(data, id, pId) {
const [result] = data.reduce(
([r, map], item) => {
const d = { ...item, children: [] };
const loc = map[item[pId]];
if (loc) {
loc.children.push(d);
} else {
r.push(d);
}
map[item[id]] = d;
return [r, map];
},
[[], {}]
);
return result;
}
//console.log(JSON.stringify(tree(source, "Id", "Parent"), null, 4));
document.querySelector("pre").innerHTML = JSON.stringify(tree(source, "Id", "Parent"), null, 4)
.as-console {
min-height: 100% !important;
}
.as-console-row {
color: blue !important;
}
.code {
<pre class="code"></pre>
arr1 = [
{
name: "will",
value: "1"
},
{
name: "nelson",
value: "3"
}
];
and
arr2 = [
{
name: "will",
value: 1,
submenu: [
{
name: "ralph",
value: 2
}
]
}
]
then remove ralph from the second array. i already created a function who do it but just check the first element and not verify the submenu.
comparador(arrSecundario) {
return (arrAtual) => {
return arrSecundario.filter(function (other) {
return other.value === arrAtual.value;
}).length !== 0;
};
}
this.arr2.filter(this.comparador(this.arr1));
Need to map array after filter. You can do like this and can also add "prop === 'submenu'" for specifically checking submenu inner array only.
var arr1 = [
{
name: "will",
value: "1"
},
{
name: "nelson",
value: "3"
}
];
var arr2 = [
{
name: "will",
value: 1,
submenu: [
{
name: "ralph",
value: 2
},
// {
// name: "nelson",
// value: 3
// }
]
}
];
function filterComparador(arrSecundario) {
return (arrAtual) => {
return arrSecundario.filter((other) => {
return other.value == arrAtual.value;
}).length != 0;
};
}
function mapComparador(arrSecundario) {
return (arrAtual) => {
Object.keys(arrAtual).forEach((prop) => {
if (Array.isArray(arrAtual[prop])) {
let propValue = arrAtual[prop].filter(this.filterComparador(arrSecundario));
if (propValue.length > 0) {
arrAtual[prop] = propValue.map(this.mapComparador(this.arrSecundario));
} else {
delete arrAtual[prop];
}
}
});
return arrAtual;
};
}
var manipulatedArray = this.arr2.filter(this.filterComparador(this.arr1))
.map(this.mapComparador(this.arr1));
console.log(manipulatedArray);
I have the following array of deeply nested objects:
const data = [
{
name: "foo",
children:[
{
count: 1,
name: "A"
},
{
count: 2,
name: "B"
}
]
},
{
name: "bar",
children: [
{
count: 3,
name: "C",
children: [
{
count: 4,
name: "D"
}
]
}
]
}
]
The way I'd like to transform this would be such as:
const expectedStructure = [
{
count: 1,
name: "A",
label: "foo = A"
},
{
count: 2,
name: "B",
label: "foo = B"
},
{
count: 3,
name: "C",
label: "bar = C"
},
{
count: 4,
name: "D",
label: "bar = D"
}
]
I created recursive function that transforms nested array into array of flat objects.
Here's my code:
function getChildren(array, result=[]) {
array.forEach(({children, ...rest}) => {
result.push(rest);
if(children) {
getChildren(children, result);
}
});
return result;
}
And here's output I get:
[ { name: 'foo' },
{ count: 1, name: 'A' },
{ count: 2, name: 'B' },
{ name: 'bar' },
{ count: 3, name: 'C' },
{ count: 4, name: 'D' } ]
The problem is that I need to add label field to every object in my output array, and I can't find a solution without iterating multiple times through the final array to make desired transformation. How to properly insert label field without hugely augmenting complexity of the function?
Check each iteration whether the current item is a "parent" item, and reassign label if it is.
const data = [{name:"foo",children:[{count:1,name:"A"},{count:2,name:"B"}]},{name:"bar",children:[{count:3,name:"C",children:[{count:4,name:"D"}]}]}];
function getChildren(array, result = [], label = "") {
array.forEach(({ children, name, count }) => {
if (!label || name[1]) {
label = `${name} = `;
}
if (count) {
result.push({ count, name, label: label + name });
}
if (children) {
getChildren(children, result, label);
}
});
return result;
}
const res = getChildren(data);
console.log(res);
You can use a different function for the nested levels, so you can pass the top-level name properties down through all those recursion levels.
function getTopChildren(array, result = []) {
array.forEach(({
name,
children
}) => {
if (children) {
getChildren(children, name, result);
}
});
return result;
}
function getChildren(array, name, result) {
array.forEach(({
children,
...rest
}) => {
rest.label = `${name} = ${rest.name}`;
result.push(rest);
if (children) {
getChildren(children, name, result);
}
});
}
const data = [{
name: "foo",
children: [{
count: 1,
name: "A"
},
{
count: 2,
name: "B"
}
]
},
{
name: "bar",
children: [{
count: 3,
name: "C",
children: [{
count: 4,
name: "D"
}]
}]
}
]
console.log(getTopChildren(data));
You can also do this recursively with flatMap based on whether or not a parent has been passed into the recursive call :
const data = [{
name: "foo",
children: [{
count: 1,
name: "A"
},
{
count: 2,
name: "B"
}
]
},
{
name: "bar",
children: [{
count: 3,
name: "C",
children: [{
count: 4,
name: "D"
}]
}]
}
];
function flatten(arr, parent = null) {
return parent
? arr.flatMap(({name, count, children}) => [
{name, count, label: `${parent} = ${name}`},
...flatten(children || [], parent)
])
: arr.flatMap(({name, children}) => flatten(children || [], name));
}
console.log(flatten(data));
Sometimes it's a little easier to reason about the code and write it clearly using generators. You can yield* from the recursive calls:
const data = [{name: "foo",children:[{count: 1,name: "A"},{ count: 2,name: "B"}]},{name: "bar",children: [{count: 3,name: "C",children: [{count: 4,name: "D"}]}]}]
function* flat(input, n){
if (!input) return
if (Array.isArray(input)) {
for (let item of input)
yield* flat(item, n)
}
let _name = n || input.name
if ('count' in input) {
yield { count:input.count, name:input.name, label:`${_name} = ${input.name}`}
}
yield* flat(input.children, _name)
}
let g = [...flat(data)]
console.log(g)
The function returns a generator, so you need to spread it into a list [...flat(data)] if you want a list or iterate over it if you don't need to store the list.
Is there a way good way JS/ES6 to loop through an object and it's children and creating new object tree array.
I have this json tree object:
[
{
id: "001",
deparmentsIds: [
"002",
"003"
],
details: {
parentDeparmentsId: null,
name: "Top"
}
},
{
id: "002",
deparmentsIds:[
"004"
],
details: {
parentDeparmentsId: ["001"],
name: "Operations"
}
},
{
id: "003",
deparmentsIds:[]
details: {
parentDeparmentsId: ["001"],
name: "Support"
}
},
{
id: "004",
deparmentsIds:[]
details: {
parentDeparmentsId: ["002"],
name: "Support operations"
}
}
]
I want to create new object array tree that looks like this:
You could create recursive function with reduce and map method to create nested object structure.
const data = [{"id":"001","deparmentsIds":["002","003"],"details":{"parentDeparmentsId":null,"name":"Top"}},{"id":"002","deparmentsIds":["004"],"details":{"parentDeparmentsId":"001","name":"Operations"}},{"id":"003","deparmentsIds":[],"details":{"parentDeparmentsId":"001","name":"Support"}},{"id":"004","deparmentsIds":[],"details":{"parentDeparmentsId":"002","name":"Support operations"}}]
function tree(input, parentId) {
return input.reduce((r, e) => {
if (e.id == parentId || parentId == undefined && e.details.parentDeparmentsId == null) {
const children = [].concat(...e.deparmentsIds.map(id => tree(input, id)))
const obj = {
[e.details.name]: children
}
r.push(obj)
}
return r;
}, [])
}
const result = tree(data)
console.log(result)
You could collect all information in an object with a single loop and return only the nodes with no parent.
function getTree(data, root) {
var o = {};
data.forEach(({ id, details: { parentDeparmentsId: parent, name } }) => {
var temp = { id, name };
if (o[id] && o[id].children) {
temp.children = o[id].children;
}
o[id] = temp;
o[parent] = o[parent] || {};
o[parent].children = o[parent].children || [];
o[parent].children.push(temp);
});
return o[root].children;
}
var data = [{ id: "001", deparmentsIds: ["002", "003"], details: { parentDeparmentsId: null, name: "Top" } }, { id: "002", deparmentsIds: ["004"], details: { parentDeparmentsId: ["001"], name: "Operations" } }, { id: "003", deparmentsIds: [], details: { parentDeparmentsId: ["001"], name: "Support" } }, { id: "004", deparmentsIds: [], details: { parentDeparmentsId: ["002"], name: "Support operations" } }],
tree = getTree(data, null);
console.log(tree);
.as-console-wrapper { max-height: 100% !important; top: 0; }