I'm still new to recursive functions but I'm having trouble returning the object once found to a variable (currently searching based on ID). I've included a dataset below and what I have so far. The recursive function finds the correct matching item, but when it returns it, it just returns undefined to the variable. I have tried the solution here and also get the same problem I have where it just returns undefined instead of the object. Any help/pointers would be great!
const data = {
"navItems": [
{
"type": "directory",
"id" : 1,
"name": "Nav Title 1",
"children": [
{
"downloadUrl": "",
"content": "",
"id" : 2,
"type": "file",
"name": "File 1.pdf"
},
{
"downloadUrl": "",
"content": "",
"type": "file",
"id" : 3,
"name": "File 2.pdf"
},
{
"type": "directory",
"name": "Sub Title 1",
"id" : 4,
"children": [
{
"downloadUrl": "",
"content": "",
"type": "file",
"id" : 5,
"name": "Sub File 1.pdf"
},
{
"downloadUrl": "",
"content": "",
"type": "file",
"id" : 6,
"name": "Sub File 2.docx"
}
]
},
{
"type": "directory",
"name": "Sub Title 2",
"id" : 7,
"children": [
{
"type": "directory",
"id" : 8,
"name": "Sub Sub Title 1",
"children": [
{
"downloadUrl": "",
"id" : 9,
"content": "",
"type": "file",
"name": "Sub Sub File 1.pdf"
},
{
"downloadUrl": "",
"content": "",
"type": "file",
"id" : 10,
"name": "Sub Sub File 2.pdf"
}
]
},
{
"type": "directory",
"name": "Sub Sub Title 2",
"id" : 11,
"children": [
{
"downloadUrl": "",
"content": "",
"id" : 12,
"type": "file",
"name": "Sub Sub File 1.pdf"
},
{
"downloadUrl": "",
"content": "",
"id" : 13,
"type": "file",
"name": "Sub Sub File 2.pdf"
}
]
}
]
}
]
}
]
}
/* console.log(navigationConfig);*/
const searchNavItems = (navItem) => {
if (navItem.id == 10) {
console.log(navItem);
return navItem;
} else {
if (navItem.hasOwnProperty("children") && navItem.children.length > 0 && navItem.type == "directory") {
navItem.children.map(child => searchNavItems(child))
} else {
return false;
}
}
};
let dataItem = data.navItems.forEach((item => {
let nav = searchNavItems(item);
console.log(nav);
}))
console.log(dataItem)
in your fn there are couple of problems
when you are maping over children you are not returning the array of children
forEach does not return anything () => void, so you will need to create a new variable to hold that value
let dataItem;
data.navItems.forEach((item) => {
console.log(searchNavItems(item));
dataItem = searchNavItems(item);
});
hope this helps
Presented below is one possible way to achieve the desired objective.
Code Snippet
const mySearch = (needle, hayStack) => (
hayStack.some(({ id }) => id === needle)
? (
{children, ...rest} = hayStack.find(({ id }) => id === needle),
[{...rest}]
)
: hayStack.flatMap(
({ children = [] }) => mySearch(needle, children)
)
);
/* explanation of the above method
// method to search for element/s with given "id"
// finding "needle" (ie "id") in hayStack (ie, "array" of objects)
const mySearch = (needle, hayStack) => (
// check if needle exists in current array
hayStack.some(({ id }) => id === needle)
? ( // find the matching array elt, destructure to access
// "children" and "rest" props.
// send the props other than "children"
{children, ...rest} = hayStack.find(({ id }) => id === needle),
[{...rest}]
) // if needle is not present in current array
// try searching in the inner/nested "children" array
: hayStack.flatMap( // use ".flatMap()" to avoid nested return
// recursive call to "mySearch" with "children" as the hayStack
({ children = [] }) => mySearch(needle, children)
)
);
*/
const data = {
"navItems": [{
"type": "directory",
"id": 1,
"name": "Nav Title 1",
"children": [{
"downloadUrl": "",
"content": "",
"id": 2,
"type": "file",
"name": "File 1.pdf"
},
{
"downloadUrl": "",
"content": "",
"type": "file",
"id": 3,
"name": "File 2.pdf"
},
{
"type": "directory",
"name": "Sub Title 1",
"id": 4,
"children": [{
"downloadUrl": "",
"content": "",
"type": "file",
"id": 5,
"name": "Sub File 1.pdf"
},
{
"downloadUrl": "",
"content": "",
"type": "file",
"id": 6,
"name": "Sub File 2.docx"
}
]
},
{
"type": "directory",
"name": "Sub Title 2",
"id": 7,
"children": [{
"type": "directory",
"id": 8,
"name": "Sub Sub Title 1",
"children": [{
"downloadUrl": "",
"id": 9,
"content": "",
"type": "file",
"name": "Sub Sub File 1.pdf"
},
{
"downloadUrl": "",
"content": "",
"type": "file",
"id": 10,
"name": "Sub Sub File 2.pdf"
}
]
},
{
"type": "directory",
"name": "Sub Sub Title 2",
"id": 11,
"children": [{
"downloadUrl": "",
"content": "",
"id": 12,
"type": "file",
"name": "Sub Sub File 1.pdf"
},
{
"downloadUrl": "",
"content": "",
"id": 13,
"type": "file",
"name": "Sub Sub File 2.pdf"
}
]
}
]
}
]
}]
};
console.log(
'data for id: 10\n',
mySearch(10, data.navItems)?.[0]
);
console.log(
'data for id: 13\n',
mySearch(13, data.navItems)?.[0]
);
console.log(
'data for id: 9\n',
mySearch(9, data.navItems)?.[0]
);
console.log(
'data for id: 3\n',
mySearch(3, data.navItems)?.[0]
);
.as-console-wrapper { max-height: 100% !important; top: 0 }
Explanation
Inline comments added to the snippet above.
Related
Hi I have below code running, but I would like to add one more property called path which should consist all its parent node path
Expected output I need something as I have shown for cardTtile, so I need same for each node.
[
{
"id": "cardShop",
"key": "cardShop",
"title": "cardShop",
"selectable": false,
"path":cardShop"
"children": [
{
"id": "cardData",
"key": "cardData",
"title": "cardData",
"parentId": "cardShop",
"path":cardShop.cardData"
"selectable": false,
"children": [
{
"id": "cardTitle",
"key": "cardTitle",
"title": "cardTitle",
"parentId": "cardData",
"path":cardShop.cardData.cardTitle"
"isLeaf": true
},
{
"id": "cardType",
"key": "cardType",
"title": "cardType",
"parentId": "cardData",
"isLeaf": true
},
{
"id": "dtmProductName",
"key": "dtmProductName",
"title": "dtmProductName",
"parentId": "cardData",
"isLeaf": true
},
{
"id": "viewAllCards",
"key": "viewAllCards",
"title": "viewAllCards",
"parentId": "cardData",
"selectable": false,
"children": [
{
"id": "url",
"key": "url",
"title": "url",
"parentId": "viewAllCards",
"isLeaf": true
},
{
"id": "text",
"key": "text",
"title": "text",
"parentId": "viewAllCards",
"isLeaf": true
}
]
}
]
},
{
"id": "eligibilityChecker",
"key": "eligibilityChecker",
"title": "eligibilityChecker",
"parentId": "cardShop",
"selectable": false,
"children": [
{
"id": "header",
"key": "header",
"title": "header",
"parentId": "eligibilityChecker",
"isLeaf": true
},
{
"id": "subHeader",
"key": "subHeader",
"title": "subHeader",
"parentId": "eligibilityChecker",
"isLeaf": true
},
{
"id": "bulletPoints",
"key": "bulletPoints",
"title": "bulletPoints",
"parentId": "eligibilityChecker",
"isLeaf": true
}
]
}
]
}
]
I have below running code example here. I tried to persist parentKey recursively but its not giving me expected output.
const transform = data => {
const loop = (data, parent) => Object.entries(data).map(([key, value]) => {
let additional = parent? {
parentId: parent
}:{}
if(typeof value === 'object' && !Array.isArray(value)){
additional = {
...additional,
selectable: false,
children: loop(value, key)
}
}else{
additional.isLeaf = true
}
return {
id: key,
key,
title: key,
...additional
}
})
return loop(data)
}
let jsonObj = {
"data": {
"cardShop": {
"cardData": {
"cardTitle": "The Platinum Card<sup>®</sup>",
"cardType": "credit-cards",
"dtmProductName": "PlatinumCard",
"viewAllCards": {
"url": "credit-cards/all-cards",
"text": "All Cards"
}
},
"eligibilityChecker": {
"header": "Check your eligibility",
"subHeader": "The Platinum Card®",
"bulletPoints": [
"Only takes a couple of minutes to complete",
"Will not impact your credit rating",
"Allows you to apply with confidence"
]
}
}
}
}
console.log(transform(jsonObj.data))
]
You suggestion would be appreciated
Thanks
You could take another variable for path and add the actual key to it.
const transform = data => {
const loop = (data, parentId, previousPath = '') => Object
.entries(data)
.map(([key, value]) => {
const
additional = parentId ? { parentId } : {},
path = previousPath + (previousPath && '.') + key;
Object.assign(
additional,
value && typeof value === 'object' && !Array.isArray(value)
? { selectable: false, children: loop(value, key, path) }
: { isLeaf: true }
);
return { id: key, key, title: key, path, ...additional };
});
return loop(data);
}
const data = { cardShop: { cardData: { cardTitle: "The Platinum Card<sup>®</sup>", cardType: "credit-cards", dtmProductName: "PlatinumCard", viewAllCards: { url: "credit-cards/all-cards", text: "All Cards" } }, eligibilityChecker: { header: "Check your eligibility", subHeader: "The Platinum Card®", bulletPoints: ["Only takes a couple of minutes to complete", "Will not impact your credit rating", "Allows you to apply with confidence"] } } };
console.log(transform(data));
.as-console-wrapper { max-height: 100% !important; top: 0; }
I am trying to solve my last issue with my reduce function to turn a nested JSON object into a flat list to enable easier searching.
Taking the JSON Below
{
"MovementPatterns": [
{
"Id": "",
"Name": "Warm-up",
"Exercises": [
{
"Id": "",
"Name": "Lizard Stretch",
"Level": 1,
"EquipmentRequired": ""
},
{
"Id": "",
"Name": "Pigeon Stretch",
"Level": 1,
"EquipmentRequired": ""
},
{
"Id": "",
"Name": "Core Hold",
"Level": 1,
"EquipmentRequired": ""
},
{
"Id": "",
"Name": "Superman",
"Level": 1,
"EquipmentRequired": ""
}
]
},
{
"Name": "Horizontal Push",
"Id": "",
"Exercises": [
{
"Id": "",
"Name": "Wall Push-up",
"Level": 0,
"VideoUrl": "",
"EquipmentRequired": ""
},
{
"Id": "",
"Name": "Push-up",
"Level": 1,
"EquipmentRequired": ""
},
{
"Id": "",
"Name": "Tap Push-up",
"Level": 2,
"EquipmentRequired": ""
},
{
"Id": "",
"Name": "Explosive Push-up",
"Level": 3,
"EquipmentRequired": ""
}
]
}
]
}
I have used the following code:
const exercises = data.MovementPatterns.reduce(
(a, {Exercises}) => [...a, ...Exercises, ...a],
[],
);
To flattern all the exercises from each movement pattern into a pure list of exercises...This is great, but I now need to INCLUDE in that JSON for each exercise the PARENT Movement Pattern ID e.g.
[
{
"Id": "",
"Name": "Lizard Stretch",
"Level": 1,
"MovementPatternId": 1,
"EquipmentRequired": ""
},
....
{
"Id": "",
"Name": "Wall Push-up",
"Level": 1,
"MovementPatternId": 2,
"EquipmentRequired": ""
},
]
Can someone please help me figure out how to do this with my reduce function :)
Thanks
You're almost close. Just append parent's Id as MovementPatternId to the each element of Exercises.
const exercises = ab.MovementPatterns.reduce(
(a, { Exercises, Id }) => [
...a,
...Exercises.map(e => ({ ...e, MovementPatternId: Id }))
],
[]
);
I'm requesting data from a remote server with json return in format :
[
{"id":"1", "parent": "#", "text" : "Parent1"},
{"id":"2", "parent": 1, "text" : "Child1"}
{"id":"3", "parent": 2, "text" : "Child12"}
{"id":"4", "parent": 1, "text" : "Child2"}
{"id":"5", "parent": 1, "text" : "Child3"}
{"id":"6", "parent": 4, "text" : "Child21"}
]
I would like to check if the selected node is a parent. I use this code:
$('#treeview').on("select_node.jstree", function (e, data) {
var isParent = data.instance.is_parent();
alert(isParent)
});
It always returns false even when I click on PARENT.
What am I missing here ?
UPDATE
This is how I get solved the issue. But I still wonder why the methods is_parent() and is_leaf() are not working
var isParent = (data.node.children.length > 0);
To get parent
use
var isParent = (data.node.children.length > 0);
alert(isParent );
$('#treeview').jstree({
'core': {
'data': [{
"id": "1",
"parent": "#",
"text": "Parent1"
}, {
"id": "2",
"parent": 1,
"text": "Child1"
},
{
"id": "21",
"parent": 2,
"text": "Child1"
},
{
"id": "3",
"parent": 2,
"text": "Child12"
}, {
"id": "4",
"parent": 1,
"text": "Child2"
}, {
"id": "5",
"parent": 1,
"text": "Child3"
},
{
"id": "6",
"parent": 4,
"text": "Child21"
},
{
"id": "7",
"parent": '#',
"text": "Parent 2"
},
{
"id": "8",
"parent": 7,
"text": "Child"
}
]
}
});
$('#treeview').on("select_node.jstree", function(e, data) {
// var isParent = data.instance.is_parent(data);
// If you need to check if a node is a root node you can use:
var isParent = (data.node.children.length > 0);
console.log(data.node);
alert(isParent)
});
<link href="https://cdnjs.cloudflare.com/ajax/libs/jstree/3.2.1/themes/default/style.min.css" rel="stylesheet" />
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jstree/3.2.1/jstree.min.js"></script>
<div id="treeview"></div>
[
{
"children": [
{
"children": [
{
"dateAdded": 1493033302670,
"id": "1534",
"index": 0,
"parentId": "1",
"title": "data1",
"url": "data2"
},
{
"children": [
{
"dateAdded": 1489571506844,
"id": "1451",
"index": 0,
"parentId": "1401",
"title": "data3",
"url": "data4"
}
],
"dateAdded": 1490363326576,
"dateGroupModified": 1490363326576,
"id": "1401",
"index": 1,
"parentId": "1",
"title": "daily"
},
{
"children": [
{
"dateAdded": 1481787664555,
"id": "1429",
"index": 0,
"parentId": "1407",
"title": "data56",
"url": "data"
},
{
"dateAdded": 1483365608504,
"id": "1430",
"index": 1,
"parentId": "1407",
"title": "data34",
"url": "data55"
}
]
}
]
}
]
}
]
This is a representation of Chrome bookmarks data.
If the object has url property it means that is a bookmark. If it does not have url property it is a folder.
It is a tree structure.
I would like to create flatten object with additional property named type. Like:
[
{
"dateAdded": 1489571506844,
"id": "1451",
"index": 0,
"parentId": "1401",
"title": "title",
"url": "some url",
"type": "bookmark"
},
{
"dateAdded": 1489571506844,
"id": "1451",
"index": 0,
"parentId": "1402",
"title": "title2",
"url": "some url2"
"type": "folder"
}
]
Thanks in advance.
You could use an iterative and recursive approach for getting flat data.
function flatten(array) {
var result = [];
array.forEach(function iter(o) {
var temp = {},
keys = Object.keys(o);
if (keys.length > 1) {
keys.forEach(function (k) {
if (k !== 'children') {
temp[k] = o[k];
}
});
temp.type = 'url' in o ? 'bookmark' : 'folder';
result.push(temp);
}
Array.isArray(o.children) && o.children.forEach(iter);
});
return result;
}
var data = [{ children: [{ children: [{ dateAdded: 1493033302670, id: "1534", index: 0, parentId: "1", title: "data1", url: "data2" }, { children: [{ dateAdded: 1489571506844, id: "1451", index: 0, parentId: "1401", title: "data3", url: "data4" }], dateAdded: 1490363326576, dateGroupModified: 1490363326576, id: "1401", index: 1, parentId: "1", title: "daily" }, { children: [{ dateAdded: 1481787664555, id: "1429", index: 0, parentId: "1407", title: "data56", url: "data" }, { dateAdded: 1483365608504, id: "1430", index: 1, parentId: "1407", title: "data34", url: "data55" }] }] }] }];
console.log(flatten(data));
.as-console-wrapper { max-height: 100% !important; top: 0; }
I've made a function that iterates through an array containing objects. If a given object has a property called children, the function calls itself. If it doesn't, then it gets pushed to a new array flattenedBookmarks.
The Solution
var flattenedBookmarks = [];
flattenBookmarks(bookmarks);
function flattenBookmarks(bookmarks) {
for (var i = 0; i < bookmarks.length; i++) {
var potentialBookmark = bookmarks[i];
if (potentialBookmark.hasOwnProperty("url")) {
potentialBookmark.type = "bookmark";
} else {
potentialBookmark.type = "folder";
}
if (potentialBookmark.hasOwnProperty("children")) {
flattenBookmarks(potentialBookmark.children);
if (potentialBookmark.hasOwnProperty("dateGroupModified")) {
flattenedBookmarks.push(potentialBookmark);
}
} else {
flattenedBookmarks.push(potentialBookmark);
}
}
}
You should probably be returning the flattened array from the function instead of storing it in a new global array flattenedBookmarks, but at least this will get you started.
https://jsfiddle.net/s9ur35re/
The example shows how to do it
data = [
{
"children": [
{
"children": [
{
"dateAdded": 1493033302670,
"id": "1534",
"index": 0,
"parentId": "1",
"title": "data1",
"url": "data2"
},
{
"children": [
{
"dateAdded": 1489571506844,
"id": "1451",
"index": 0,
"parentId": "1401",
"title": "data3",
"url": "data4"
}
],
"dateAdded": 1490363326576,
"dateGroupModified": 1490363326576,
"id": "1401",
"index": 1,
"parentId": "1",
"title": "daily"
},
{
"children": [
{
"dateAdded": 1481787664555,
"id": "1429",
"index": 0,
"parentId": "1407",
"title": "data56",
"url": "data"
},
{
"dateAdded": 1483365608504,
"id": "1430",
"index": 1,
"parentId": "1407",
"title": "data34",
"url": "data55"
}
]
}
]
}
]
}
];
data2 = [];
function search(data) {
for (n in data) {
if (typeof data[n] == 'object') {
if (data[n].id != undefined) {
if (data[n].url != undefined) {
data[n].type="folder";
} else {
data[n].type="bookmark";
}
data2.push(data[n]);
}
search(data[n]);
}
}
}
search(data);
console.log(data2);
hello i want to use my mysql result to build the json objects. my first query is fetching parent menu and another query is fetching child menu so i am going to put my child menu result under the parent menu.. i want to know what is wrong with my code and how to correct
<?php
$selectparentMenu=mysql_query("SELECT `id`,`item_name`,`menu_type`,`parent` FROM `epic_master_menu` where parent=0");
//echo $myfetch=mysql_fetch_array($selectMenu);
if(mysql_num_rows($selectparentMenu)>1) {
while($fetchparentMenu=mysql_fetch_array($selectparentMenu)) {
//echo 'parent id is' .$parentId=$fetchparentMenu['id']. '<br/>';
// echo 'parent name is' .$parentId=$fetchparentMenu['item_name']. '<br/>';
$selectchildMenu=mysql_query("SELECT `id` , `item_name` , `menu_type` , `parent` FROM `epic_master_menu`
WHERE parent >0 AND `menu_type` = 'item' AND `parent` ='".$fetchparentMenu['id']."'");
if(mysql_num_rows($selectchildMenu)>1) {
while($fetchchildMenu=mysql_fetch_array($selectchildMenu)) {
$textjson = '{
"dataSource": [{
"id": "", "text": "Select All", "expanded": "true", "spriteCssClass": "rootfolder", "items": [
{
"id": "'.$fetchparentMenu["id"].'", "text": "'.$fetchparentMenu["item_name"].'", "expanded": true,"spriteCssClass": "folder", "items": [
{ "id": "'.$fetchchildMenu["id"].'", "text": "'.$fetchparentMenu["item_name"].'", "spriteCssClass": "html" },
{ "id": "'.$fetchchildMenu["id"].'", "text": "'.$fetchparentMenu["item_name"].'", "spriteCssClass": "html" },
{ "id": "'.$fetchchildMenu["id"].'", "text": "'.$fetchparentMenu["item_name"].'", "spriteCssClass": "image" }
]
}
]
}]
}';
} }
/* $fetchMenu['item_name'];
$fetchMenu['menu_type'];
$fetchMenu['parent'];*/
//print_r($fetchMenu);
} }
// my json data is
$textjson = '{
"dataSource": [{
"id": 1, "text": "My Documents", "expanded": "true", "spriteCssClass": "rootfolder", "items": [
{
"id": 2, "text": "Project", "expanded": true,"spriteCssClass": "folder", "items": [
{ "id": 3, "text": "about.html", "spriteCssClass": "html" },
{ "id": 4, "text": "index.html", "spriteCssClass": "html" },
{ "id": 5, "text": "logo.png", "spriteCssClass": "image" }
]
},
{
"id": 6, "text": "New Web Site", "expanded": true, "spriteCssClass": "folder", "items": [
{ "id": 7, "text": "mockup.jpg", "spriteCssClass": "image" },
{ "id": 8, "text": "Research.pdf", "spriteCssClass": "pdf" }
]
},
{
"id": 9, "text": "Reports", "expanded": true, "spriteCssClass": "folder", "items": [
{ "id": 10, "text": "February.pdf", "spriteCssClass": "pdf" },
{ "id": 11, "text": "March.pdf", "spriteCssClass": "pdf" },
{ "id": 12, "text": "April.pdf", "spriteCssClass": "pdf" }
]
}
]
}]
}';
Instead of creating your own version.. you could simply make use of json_encode()
while($fetchchildMenu=mysql_fetch_array($selectchildMenu))
{
$somearr[]=$fetchchildMenu;
}
$jsonData = json_encode($somearr);
echo $jsonData; //<---- Prints your JSON data
Its much easier to use the included json functions from PHP. Here you can use json_encode to create a json string from an array.
$childArray = array();
while($fetchchildMenu=mysql_fetch_array($selectchildMenu)) {
$childArray[] = array(
'id' => $fetchchildMenu['id'],
'text' => $fetchchildMenu['text']
);
}
$jsonDataChilds = json_encode($childArray);
echo $jsonDataChilds;