I have the below object and if I send some array I need the parents of those object.
Lets say if I send {Id:113} , It should return [11,1]
[
{
Id:1,
Name:"Test",
children:[
{
Id:11,
Name:"Test",
children:[
{
Id:113,
Name:"Test",
children:[
]
},
{
Id:114,
Name:"Test",
children:[
]
}
]
},
{
Id:12,
Name:"Test",
children:[
]
},
{
Id:13,
Name:"Test",
children:[
{
Id:115,
Name:"Test",
children:[
{
Id:1111,
Name:"Test",
children:[
]
}
]
}
]
},
{
Id:14,
Name:"Test",
children:[
]
}
]
} {
Id:2,
Name:"Test",
children:[
]
}
]
I am able to fetch each children item by using the below code.
this.selectedOMStructure = function (structure, id) {
_.each(structure, function (_item) {
if (_item.Id == id) {
_item.isChecked = !_item.isChecked;
if (_item.children) {
_this.checkAllChildren(_item.children, _item.isChecked);
} else {
return;
}
} else if (_item.children) {
_this.selectedOMStructure(_item.children, id);
}
})
}
this.checkAllChildren = function (structure, value) {
_.each(structure, function (_item) {
_item.isChecked = value;
if (_item.children) {
_this.checkAllChildren(_item.children, value);
} else {
return;
}
})
}
But I have no idea how to find the parents of mentioned ID
I am not sure if this is a better approach. Try this,
data will be having your array. main() is the entry point. And the value will be stored in a.
var a = [];
function main() {
a = [];
for (let i = 0; i < data.length; i++) {
checkChildren(data[i], 1111);
}
console.log(a);
}
function checkChildren(node, id) {
if (node.Id == id) {
return id;
}
else if (node.children) {
for (let i = 0; i < node.children.length; i++) {
let r = checkChildren(node.children[i], id);
if (r != 0) {
a.push(node.Id);
return node.Id;
}
}
return 0;
}
}
main();
You will need to map the values from this data structure into a new data structure.
Consider using a recursive algorithm that attaches a new parent field to every item while also adding it to a map from id to the item.
Related
I need to restructure a nested JSON data.
Here is how it looks like:
{
"MainKey1": [
{
"Section1": {
"ParentTag1 Mapped Label": {
"ParentTag1": {
"Tag1 Mapped Label": {
"Tag1": "1234567890"
}
}
}
}
},
{
"Section2": {
"ParentTag1 Mapped Label": {
"ParentTag1": {
"Tag1 Label": {
"Tag1": "111222333444"
},
"Tag2 Label": {
"Tag2": "121212"
},
"Tag3 Label": {
"Tag3": "0987654321"
}
}
}
}
}
],
"MainKey2": [
{
"Section1": {
"ParentTag1 Mapped Label": {
"ParentTag1": {
"Tag1 Mapped Label": {
"Tag1": "1234567890"
}
}
}
}
}
]
}
And this is a sample of the converted JSON:
{
MainKey: [
{
Section1: [
{
ParentTag1: [
{ Tag1: "1234567890" }
]
}
]
},
{
Section2: [
{
ParentTag1: [
{ Tag1: "111222333444" },
{ Tag2: "121212" },
{ Tag3: "0987654321" }
]
}
]
}
],
MainKey2: [
{
Section1: [
{
ParentTag1 : [
{ Tag1: "1234567890" }
]
}
]
}
]
}
Rules:
Everything inside a MainKey (outermost keys, could be any name) should be an array
All labels should be stripped (as the label could be any name, without the actual word "Label", we can determine if it is a label based on the depth level. Since the JSON will have the label as the parent and the actual "tag" as a child.
Here is what I currently have (it is a mess, sorry!)
function convertJson (jsonObj) {
const mainKeys = Object.keys(jsonObj)
let output = {}
for (let i = 0; i < mainKeys.length; i++) {
const mainKey = mainKeys[i]
let result = []
output[mainKey] = result
for (let j = 0; j < jsonObj[mainKey].length; j++) {
const innerObj = {...jsonObj[mainKey][j]}
const sectionName = Object.keys(innerObj)[0]
const sectionObj = {}
sectionObj[sectionName] = []
const index = result.push(sectionObj) - 1
parseObj(innerObj[sectionName], result[index], 0) // if I change 2nd param to: result, it generates incorrect output
}
}
console.log(output)
}
function parseObj (innerObj, result, depthCount) {
for (var key in innerObj) {
if (typeof innerObj[key] === "object") {
if (depthCount % 2 === 1) {
const parentObj = {}
parentObj[key] = []
result.push(parentObj)
depthCount++
parseObj(innerObj[key], parentObj[key], depthCount)
} else {
depthCount++
parseObj(innerObj[key], result, depthCount)
}
} else {
const keyValuePairObj = {}
keyValuePairObj[key] = innerObj[key]
result.push(keyValuePairObj)
}
}
return result
}
convertJson(json)
But it generates an error:
Uncaught TypeError: result.push is not a function
Now if I change line 90 from:
parseObj(innerObj[sectionName], result[index], 0)
to:
parseObj(innerObj[sectionName], result, 0)
Here is incorrect output:
{
"MainKey1": [
{
"Section1": []
},
{
"ParentTag1": [
{
"Tag1": "1234567890"
}
]
},
{
"Section2": []
},
{
"ParentTag1": [
{
"Tag1": "111222333444"
},
{
"Tag2 Label": [
{
"Tag2": "121212"
}
]
},
{
"Tag3": "0987654321"
}
]
}
],
"MainKey2": [
{
"Section1": []
},
{
"Tag1": "1234567890"
}
]
}
And here is my fiddle:
https://jsfiddle.net/kzaiwo/L4avxmyd/36/
Thanks a lot! Appreciate any help!
i have an array of objects of the below format
each with a unique 'sub-task' entry, each of this sub-task is to be embedded as a children element of each unique 'task' from the 'tasks' array
[
{
"sub-task":"abc",
"task":"alpha1"},
{
"sub-task":"def",
"task":"alpha1"},
{
"sub-task":"ijkl",
"task":"proto"},
{
"sub-task":"mno",
"task":"def"},
{
"sub-task":"qrs",
"task":"proto"},
{
"sub-task":"asdf",
"task":"mno"},
]
i was trying to frame an another array of below format
[
{
"name":"alpha1",
"children":[
{
"name":"abc"
},
{
"name":"def",
"children":[
{
"name":"mno"
}
]
}
]
},
{
"name":"proto",
"children":[
{
"name":"ijkl"
},
{
"name":"qrs",
"children":[
{
"name":"asdf"
}
]
}
]
}
]
i was trying of below logic, but ended up with no solution...
var treeData = [];
for( var ele of tasks){
recurOn(treeData,ele);
}
function recurOn(arr,obj){
if(arr.length == 0){
treeData.push({name:obj.parentGroup,children:[{name:obj.groupName}]})
//console.log(treeData);
return 1;
}else {
for(var item of treeData){
if(item.name == obj.parentGroup){
//console.log('item: ', item);
item.children.push({name:obj.groupName});
break;
}
else {
treeData.push(recurOn([],obj))
}
}
return 1;
}
}
//console.log(treeData);
//console.log(result);
Since the no of levels an elements holds is not known i was unable to fix for a logic
Use a map to store object reference.
let input = [
{ "sub-task": "abc", "task": "alpha1" },
{ "sub-task": "def", "task": "alpha1" },
{ "sub-task": "ijkl", "task": "proto" },
{ "sub-task": "mno", "task": "def" },
{ "sub-task": "qrs", "task": "proto" },
{ "sub-task": "asdf", "task": "mno" },
];
let map = new Map, result = [];
input.forEach(({ ["sub-task"]: name, task }) => {
let node = map.get(task), child = { name, children: [] };
if (!node) {
map.set(task, node = { name: task, children: [] });
result.push(node);
}
map.set(name, child);
node.children.push(child);
})
console.log(result);
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; }
Can anyone show me how to parse an array of URL-like data to json?
Array ["a.b.c.d","a.c.e.f","a.b.c.g"] to this kind of json:
items:{
text: "a",
items:[
{
text:"b",
items:[
{
text:"c",
items:[
{
text:"d",
leaf:true
},
{
text:"g",
leaf:true
}
]
}
]
},
{
text:"c",
items:[
{
text:"e",
items:[
{
text:"f",
leaf:true
}
]
}
]
}
]
}
The following should work:
// ['a', 'b'] -> { text: 'a', items: [{ text: 'b', leaf: true }] }
function buildTree(components) {
if (components.length === 0) {
throw new Error('Can\'t parse: empty components');
} else if (components.length === 1) {
return { text: components[0], leaf: true };
} else {
return {
text: components[0],
items: [buildTree(components.slice(1))]
}
}
}
// 'a.b' -> { text: 'a', items: [{ text: 'b', leaf: true }] }
function parseString(str) {
return buildTree(str.split('.'));
}
// Merge nodes with a same text.
function mergeSame(left, right) {
if (left.text !== right.text) {
throw new Error('Can\'t merge: different text ' + left.text + ', ' + right.text);
}
// Same text
if (left.leaf && right.leaf) {
return left;
} else if (left.leaf && !right.leaf) {
return right;
} else if (!left.leat && right.leaf) {
return left;
} else {
var concat = left.items.concat(right.items);
return { text: left.text, items: merge(concat) };
}
}
// Merge multiple nodes.
function merge(items) {
var textToItem = {};
var keys = [];
for (var i = 0; i < items.length; i++) {
var text = items[i].text;
if (textToItem[text]) {
textToItem[text] = mergeSame(textToItem[text], items[i]);
} else {
textToItem[text] = items[i];
keys.push(text);
}
}
keys.sort();
var merged = [];
for (i = 0; i < keys.length; i++) {
merged.push(textToItem[keys[i]]);
}
return merged;
}
function parse(strs) {
var nodes = [];
for (var i = 0; i < strs.length; i++) {
nodes.push(parseString(strs[i]));
}
return { items: merge(nodes) };
}
console.log(parseString('a.b.c.d'));
console.log(parse(["a.b.c.d","a.c.e.f","a.b.c.g"]));
It may look messy. I was not sure about your environment and didn't use map or reduce.
You can use this demo.
Just remove unnecessary formatting ;) and change to local variables as you want.
I have written just parsing logic for you.
function parceArrayToExtJSTreeNodes(arr) {
for(i in arr) {
addNodesToTree(arr[i].split('.'));
}
createExtJSString(root, 0, true);
}
http://jsfiddle.net/HxFL6/