I have a list:
var list = ['parent-element', 'child-of-previus-element-1', 'child-of-previus-element-2'];
where each next element in the array is a child of the previous.
I want to transform this list into a tree structure, e.g.:
{
"parent-element": {
"childrens": [{
"child-of-previus-element-1": {
"childrens": [{
"child-of-previus-element-2": {
"childrens": []
}
}]
}
}]
}
}
I have tried:
var list = ['parent-element', 'child-of-previus-element-1', 'child-of-previus-element-2'];
var tree = {};
for (var i = 0; i < list.length; i++) {
if( list[i-1] && tree[list[i-1]] ){
tree[list[i-1]].childrens[list[i]] = {"childrens": []};
} else {
tree[list[i]] = {
"childrens": []
};
}
}
console.log( JSON.stringify(tree) );
but the output is:
{
"parent-element":{
"childrens":[]
},
"child-of-previus-element-2":{
"childrens":[]
}
}
You might reduce it right:
var list = ['parent-element', 'child-of-previus-element-1', 'child-of-previus-element-2'];
var tree = list.reduceRight((child, key) => ({ [key]: { children: [child] } }), null);
console.log(JSON.stringify(tree))
Related
hi everyone i have data given below
nodes data
var nodes=[
{
name:'shanu',
value:5
},
{
name:'bhanu',
value:2
},
{
name:aaditya,
value:1
}
]
edge data
var edge =[
{
source:'shanu',
target:'aaditya'
},
{
source:'shanu',
target:'bhanu'
},
{
source:'aaditya',
target:'bhanu'
}
]
Now I just want to convert edge data source and target based on id
output I want from above data
var convertedEdge=[
{
source:0,
target:2
},
{
source:0,
target:1
},
{
source:1,
target:2
}
]
you need to use two loops:
var convertedEdge = [], source = '', target = '';
for (i in edge) {
source = edge[i].source;
target = edge[i].target;
for(j in nodes) {
if (source == nodes[j].name) {
source = j
}
if (target == nodes[j].name) {
target = j
}
}
convertedEdge.push({
source: source,
target: target
})
}
I want to get my file directories as nested array object but i can't seem to figure out how to convert
[
'routes/files',
'routes/files/emptyfolder',
'routes/files/somefolder',
'routes/files/somefolder/example.docx',
'routes/files/test.docx',
'routes/randomdata.json'
]
to
[
{
title: 'routes',
content: [
{
title: 'files',
content: [
{
title: 'empty folder',
content: []
},
{
title: 'somefolder',
content: [
{
title: 'example.docx',
},
]
},
{
title: 'test.docx',
}
],
},
{
title: 'randomdata.json'
}
],
}
]
it looks impossible problem for me to solve.
I would love to know how to solve it.
Thank you.
Here is how I solved it:
Not the best solution, but works.
const arr = [
"routes/files",
"routes/files/emptyfolder",
"routes/files/somefolder",
"routes/files/somefolder/example.docx",
"routes/files/test.docx",
"routes/randomdata.json",
];
const arr2 = arr.map((p) => p.split("/"));
const setNestedObjectField = (
obj,
props,
value
) => {
if (!Array.isArray(obj)) {
if (!obj.content) {
obj.content = [];
}
obj = obj.content;
}
for (const propName of props) {
const next = obj.find((el) => el.title === propName);
if (!next) {
console.assert(props.at(-1) === propName);
// last propName
obj.push(value);
} else {
if (!next.content) {
next.content = [];
}
obj = next.content;
}
}
};
const rez = [];
let index = 0;
while (arr2.some((s) => s[index] !== undefined)) {
// arr2 = arr2.filter((a) => a.length);
const layer = arr2.reduce((acc, pathArr) => {
if (pathArr[index] === undefined) return acc;
acc.add(pathArr.slice(0, index + 1).join("/"));
return acc;
}, new Set());
// console.log({ layer });
for (const key of layer) {
setNestedObjectField(rez, key.split("/"), { title: key.split("/").at(-1) });
}
index++;
}
console.log(rez);
I came across this question and it's an interesting problem, I know it's already been answered, but I wanted to spend a little of my time to solve it my way.
here I leave my code:
function nestedDirectories (arr) {
const splittedArray = arr.map(a => a.split('/'));
return {
mergeObjs: function(target, source) {
for (let key in source) {
if(!target[key]) target[key] = {};
target[key] = this.mergeObjs(target[key], source[key]);
}
return target;
},
buildResponse: function (objMain) {
let arr = [];
for (let key in objMain) {
let o = { title: key, content: [] };
if(key.includes(".")) {
delete o.content;
} else if (Object.keys(objMain[key]).length) {
o.content = this.buildResponse(objMain[key]);
}
arr.push(o);
}
return arr;
},
exec: function () {
let targetObject = {};
splittedArray.forEach(arrParent => {
let strObj = '';
for (let i = arrParent.length - 1; i >= 0 ; i--) {
strObj = `"${arrParent[i]}": {${strObj}}`;
}
let parseObj = JSON.parse(`{${strObj}}`);
targetObject = this.mergeObjs(targetObject, parseObj);
});
return this.buildResponse(targetObject);
}
}
}
and use it like this:
const dirs = [
'routes/files',
'routes/files/emptyfolder',
'routes/files/somefolder',
'routes/files/test.docx',
'routes/randomdata.json',
'routes/files/somefolder/example.docx'
];
const data = nestedDirectories(dirs).exec();
result:
[
{
title: 'routes',
content: [
{
title: 'files',
content: [
{ title: 'emptyfolder', content: [] },
{
title: 'somefolder',
content: [ { title: 'example.docx' } ]
},
{ title: 'test.docx' }
]
},
{ title: 'randomdata.json' }
]
}
]
I have an array of objects something like this
arr = [{"class":"section"},{"fieldGroup":[]},{"class":"col","name":"input"},{"class":"col","name":"dropdown"},{"class":"col","name":"date"},{"fieldGroup":[]},{"class":"col","name":"table"}]
Now I need to move the objects with "class":"col" inside its previous object which contains "fieldGroup"
So my desired output should be like this
arr = [{"class":"section"},{"fieldGroup":[{"class":"col","name":"input"},{"class":"col","name":"dropdown"},{"class":"col","name":"date"}]},{"fieldGroup":[{"class":"col","name":"table"}]}]
I have tried this piece of code
arr.forEach((item: any, index: number) => {
if (item.class === "col") {
arr[index - 1].fieldGroup.push(item);
arr.splice(index, 1);
}else {
return arr;
}
})
but getting an error saying cannot read property push of undefined
any help?
Your problem is that when you remove an item from your array using .splice() all the items in your arr shift down one index. For example, the item that used to be at index 3 will now be at index 2, the item that used to be at index 4 is will now be at index 3 etc. Your .forEach() loop doesn't take this shift into account, and so once you remove the item at index 2 with .splice() your index changes to 3 for the next iteration, but really, you need to look at index 2 again as your array items have shifted down an index. You can fix this by using a standard for loop and decrementing the index counter when you remove an item to look at th inded again:
const arr = [{"class":"section"},{"fieldGroup":[]},{"class":"col","name":"input"},{"class":"col","name":"dropdown"},{"class":"col","name":"date"},{"fieldGroup":[]},{"class":"col","name":"table"}];
for(let i = 0; i < arr.length; i++) {
const item = arr[i];
if (item.class === "col") {
arr[i - 1].fieldGroup.push(item);
arr.splice(i, 1);
i--;
}
}
console.log(arr);
try it ...
var arr = [{"class":"section","name":"input"},{"fieldGroupName":"one","fieldGroup":[]},{"class":"col","name":"input"},{"class":"col","name":"dropdown"},{"class":"section","name":"input"},{"fieldGroupName":"one","fieldGroup":[]},{"class":"col","name":"date"}]
arr.forEach((elm , index) =>{
if(elm.class == 'col'){
arr[1].fieldGroup.push(elm)
arr.splice(index, 1)
}
})
console.log(arr);
Try this:
var arr = [{
"class": "section"
},
{
"fieldGroup": []
},
{
"class": "col",
"name": "input"
},
{
"class": "dropdown"
},
{
"class": "date"
},
{
"fieldGroup": []
},
{
"class": "col",
"name": "table"
}
];
let fieldGroupPosition;
let finalArr = [];
let lastGroupPosition = null;
arr.forEach((item, index) => {
if (Array.isArray(item.fieldGroup)) {
finalArr.push({
fieldGroup: []
});
lastGroupPosition = finalArr.length - 1;
} else {
if (lastGroupPosition)
finalArr[lastGroupPosition].fieldGroup.push(item);
else
finalArr.push(item);
}
});
console.log(finalArr);
var a = [
{ class: "section" },
{ fieldGroup: [] },
{ class: "col", name: "input" },
{ class: "dropdown" },
{ class: "date" },
{ fieldGroup: [] },
{ class: "col", name: "table" },
];
var res = [];
var currFieldGroup = null;
for (var i in a) {
if ("fieldGroup" in a[i]) {
currFieldGroup = a[i];
res.push(a[i]);
} else if ("class" in a[i]) {
if (currFieldGroup) {
currFieldGroup.fieldGroup.push(a[i]);
} else {
res.push(a[i]);
}
}
}
console.log(res);
I have an object which I'm trying to filter out elements with a path and map, but I can't get past the first level into the nested children.
My object (with UI components removed):
const items = [
{
path: "/login"
},
{
path: "/help"
},
{
name: "Guidelines",
children: [
{
name: "Section 1",
children: [
{
name: "Chapter 1",
path: "/section-1/chapter-1"
},
{
name: "Chapter 2",
path: "/section-1/chapter-2"
}
]
},
{
name: "Section 2",
children: [
{
name: "Chapter 3",
path: "/section-2/chapter-3"
},
{
name: "Chapter 4",
path: "/section-2/chapter-4"
}
]
}
]
}
];
This filters the elements with a path, but only to the first level:
const filteredRoutes = items.filter((route) => route.path);
Result:
[
{"path":"/login"},
{"path":"/help"}
]
My goal is to have a list of routes with 6 items in this Codesandbox
[
{ "path": "/login" },
{ "path": "/help" },
{ "path": "/section-1/chapter-1" },
{ "path": "/section-1/chapter-2" },
{ "path": "/section-2/chapter-3" },
{ "path": "/section-2/chapter-4" },
]
Thanks
const getPath = (x) => (x.path ? { path: x.path } : x.children?.map(getPath));
const filteredRoutes = items && items.map(getPath).flat(Infinity);
Does this solve your problem?
const filteredRoutes = [];
const arr = items.map((item) => {
if (item.path) {
filteredRoutes.push({"path" : item.path});
} else {
item.children.map((child) => {
if (child.children) {
child.children.map((_child) => {
filteredRoutes.push({"path" : _child.path});
})
}
})
}
});
console.log(filteredRoutes);
Would something like this work?
const findRoutesWithPaths = (routes) => {
if (!routes) {
return [];
}
const filteredRoutes = [];
// Loop over all the routes
routes.forEach((item) => {
// Add `path` from self
if (item.path) {
filteredRoutes.push(item);
}
// Add `path`s from children
if (item.children) {
filteredRoutes.push(...findRoutesWithPaths(item.children));
}
});
return filteredRoutes;
};
const filteredRoutes = findRoutesWithPaths(items);
codesandbox
Although you wanted to use the filter method, I found a way to iterate your array of objects recursively in case you have an unknown depth, your pathArray should have a length of 6 given the example data, but it will work of you have more children in your data as well.
var pathArray = [];
//Loop through all the objects in your items array
for (var k = 0; k < items.length; k++) {
//For each object let's gather all the paths in the object
var route = items[k];
function getPath(obj) {
//If the object has a "children" attribute then we should look inside
if (obj.hasOwnProperty("children")) {
for (var i = 0; i < obj.children.length; i++) {
getPath(obj.children[i]);
}
}
// If not then this is the base level, which means there is a path attribute we need to grab
else {
pathArray.push(obj.path); //Add the path to our array
}
}
getPath(route);
}
Let me know if you need any more clarification
I try to write a function in JavaScript which filter an array by a selected property (an value).
But it works for 2 level only I do not understand what do I missing.
The data I want to filter:
var data = [
{
name: "john_pc",
children: [
{
name: "sabrina_pc",
children: [
{
name: "sabrina_pc"
},
{
name: "john_pc"
}
]
},
{
name: "john_pc"
}
]
},
{
name: "sabrina_pc"
}
]
The childrenFilter funciton :
const childrenFilter = (childrenData, filters) => {
let filteredData = childrenData.filter(item => {
for (var property in filters) {
var optionalValues = filters[property];
var value = item[property];
if (item.children) {
item.children = childrenFilter(item.children, filters);
}
let hasValue = value == optionalValues;
if (hasValue) {
return true;
}
return false;
}
return false;
}, this);
return filteredData;
}
Calling the function:
As you can see the 'childrenFilter' get an object which the key is property in the data and the key is value I want to keep.
let result = childrenFilter(data, {
"name": "a1"
});
console.log(JSON.stringify(result, null, 2))
The wanted result :
[
{
"name": "john_pc",
"children": [
{
"name": "sabrina_pc",
"children": [
{
"name": "john_pc"
}
]
},
{
"name": "john_pc"
}
]
}
]
Your filter function does not take into account whether or not children elements match the pattern, therefore even though some child elements of the object match the pattern, the object itself is being filtered out.
Here is the explanation:
{
name: "a2", // does not match filter {name:'a1} so is removed alongside child objects
children: [ // gets removed with parent object
{
name: "a2"
},
{
name: "a1"
}
]
}
This should produce the desired output:
const childrenFilter = (childrenData, filters) => {
let filteredData = childrenData.filter(item => {
for (var property in filters) {
var optionalValues = filters[property];
var value = item[property];
if (item.children) {
item.children = childrenFilter(item.children, filters);
}
let hasValue = value == optionalValues;
if (hasValue || item.children.length) { // include item when children mathes the pattern
return true;
}
return false;
}
return false;
}, this);
return filteredData;
}
You could build new array for each step of filtering, beginning from the leaves and check if this contains the wanted value.
This approach generates new objects and does not mutate the original data.
function filter(array, filters) {
return array.reduce((r, o) => {
var children = filter(o.children || [], filters);
return children || Object.entries(filters).every(([k, v]) => o[k] === v)
? (r || []).concat(Object.assign({}, o, children && { children }))
: r;
}, undefined);
}
var data = [{ name: "a1", children: [{ name: "a2", children: [{ name: "a2" }, { name: "a1" }] }, { name: "a1" }] }, { name: "b1" }];
console.log(filter(data, { name: "a1" }));
.as-console-wrapper { max-height: 100% !important; top: 0; }