JavaScript execution differences between JavaScript and JavaScript through Ajax - javascript

I currently have a problem on my javascript code.
I want to create a treeview using this module:
jqm-tree
==Problem==
When I execute the demo code in index.html like this:
<script>
$("#tree").jqmtree({
title : 'Items',
collapsed: true,
data: [
{ "id": 1, "title": "Menu 1" },
{ "id": 2, "title": "Sub 1 1", "pid":1 },
{ "id": 3, "title": "Sub 1 2", "pid": 1 },
{ "id": 4, "title": "Sub 1 3", "pid": 1 },
{ "id": 5, "title": "Menu 2" },
{ "id": 6, "title": "Sub 2 1", "pid": 5 }
]
});
</script>
the result is correct.
but when I execute this same demo code after a ajax request like this :
$(document).ready(function() {
$.ajax({
type: "GET",
url: "data/data.csv",
dataType: "text",
success: function(data)
{
$("#tree").jqmtree({
title : 'Items',
collapsed: true,
data: [
{ "id": 1, "title": "Menu 1" },
{ "id": 2, "title": "Sub 1 1", "pid":1 },
{ "id": 3, "title": "Sub 1 2", "pid": 1 },
{ "id": 4, "title": "Sub 1 3", "pid": 1 },
{ "id": 5, "title": "Menu 2" },
{ "id": 6, "title": "Sub 2 1", "pid": 5 }
]
});
}
});
});
I have not the right result, the collapsible is no longer active on the menu 1 and menu 2. nok screenshot
 Can you point me to the reasons for this difference ?
thanks for your help.
==Edit1=
I try to put the demo code without ajax :
$(document).on('pagecreate', '#page1' ,function(){
$("#tree").jqmtree({
title : 'Items',
collapsed: true,
data: [
{ "id": 1, "title": "Menu 1" },
{ "id": 2, "title": "Sub 1 1", "pid":1 },
{ "id": 3, "title": "Sub 1 2", "pid": 1 },
{ "id": 4, "title": "Sub 1 3", "pid": 1 },
{ "id": 5, "title": "Menu 2" },
{ "id": 6, "title": "Sub 2 1", "pid": 5 }
]
});
});
The result is not good, I don't understand why ;(

Related

How to combine nested duplicate dictionary values within an array

I have a following data structure, array of dictionaries
[
{
"id": 1,
"name": "Some Name 1",
"topics": [
{
"id": 3,
"name": "Topic Name",
"topics": [],
},
{
"id": 3,
"name": "Topic Name",
"topics": [],
}
]
},
{
"id": 2,
"name": "Some Name 2",
"topics": [
{
"id": 3,
"name": "Topic Name",
"topics": [],
},
{
"id": 3,
"name": "Topic Name",
"topics": [],
}
]
},
[
{
"id": 1,
"name": "Some Name 2",
"topics": [
{
"id": 1,
"name": "Topic Name 1",
"topics": [],
},
{
"id": 2,
"name": "Topic Name 2",
"topics": [],
}
]
},
{
"id": 2,
"name": "Some Name 1",
"topics": [
{
"id": 3,
"name": "Topic Name",
"topics": [],
},
{
"id": 3,
"name": "Topic Name",
"topics": [],
}
]
},
I want to essentially be able to combine the arrays within each dictionary if I have the same id, so for each dictionary i will have higher level dictionary items being the same and essentially combine those together to avoid duplication. Preferably i would like it to be recursively being that in case the topics within topics are also duplicate (you can assume the order is always respected).
I would appreciate if you could share a solution to this

Get nearest parent object in nested array

I have the following array:
const items = [
{
"id": 1,
"name": "Menu 1",
"content": [
{
"id": 2,
"name": "Submenu 1 OF 1",
"url": "teste"
}
]
},
{
"id": 3,
"name": "Menu 2",
"content": [
{
"id": 4,
"name": "Submenu 1 OF 2",
"url": "teste"
},
{
"id": 5,
"name": "Submenu 2 OF 2",
"content": [
{
"id": 6,
"name": "Sub submenu 1 OF 2",
"url": "teste"
},
{
"id": 7,
"name": "Sub submenu 2 OF 2",
"url": "teste"
},
]
}
]
}
]
I need a function that given the id gets the closest parent object. For example, if I give the id 2 it returns {"id: 1, "name": "Menu 1", "content": [...]}. If I give the id 6 it returns {"id": 5, "name": "Submenu 2 OF 2", content: [...]}.
I have tried but i can only get the top-level parent and not the closest one.
EDIT: the code that i have tested so far:
let findDeep = function(data, id) {
return data.find(function(e) {
if(e.id == id) return e;
else if(e.content) return findDeep(e.content, id);
}
})
You can use a recursive depth-first search:
function findParent(items, id, parent=null) {
for (let item of items) {
let res = item.id === id ? parent
: item.content && findParent(item.content, id, item);
if (res) return res;
}
}
// demo: find parent of id=6 in example tree:
const items = [{"id": 1,"name": "Menu 1","content": [{"id": 2,"name": "Submenu 1 OF 1","url": "teste"}]},{"id": 3,"name": "Menu 2","content": [{"id": 4,"name": "Submenu 1 OF 2","url": "teste"},{"id": 5,"name": "Submenu 2 OF 2","content": [{"id": 6,"name": "Sub submenu 1 OF 2","url": "teste"},{"id": 7,"name": "Sub submenu 2 OF 2","url": "teste"},]}]}]
console.log(findParent(items, 6));
You can do this with a recursive function that checks each child and returns the parent if the id matches:
const findParent = (id, obj) => {
for (const child of obj.content) {
if (child.id === id) return obj;
if (!child.content) continue;
const found = findParent(id, child);
if (found) return found;
}
return undefined;
}
const items = {content: [{
"id": 1,
"name": "Menu 1",
"content": [{
"id": 2,
"name": "Submenu 1 OF 1",
"url": "teste"
}]
},
{
"id": 3,
"name": "Menu 2",
"content": [{
"id": 4,
"name": "Submenu 1 OF 2",
"url": "teste"
},
{
"id": 5,
"name": "Submenu 2 OF 2",
"content": [{
"id": 6,
"name": "Sub submenu 1 OF 2",
"url": "teste"
},
{
"id": 7,
"name": "Sub submenu 2 OF 2",
"url": "teste"
},
]
}
]
}
]
}
console.log(findParent(2, items));
console.log(findParent(6, items));

Create a new array of object based on the ids of the objects

I need to transform an array of object into new array based on the parent_id's in the list. In the below given input we have a parent_id which tells use parent of the given object.
Input:
[
{
"id": 1,
"title": "Item 1",
"parent_id": null
},
{
"id": 2,
"title": "Item 2",
"parent_id": 1
},
{
"id": 3,
"title": "Item 3",
"parent_id": 2
},
{
"id": 4,
"title": "Item 4",
"parent_id": null
},
{
"id": 5,
"title": "Item 5",
"parent_id": null
},
{
"id": 6,
"title": "Item 6",
"parent_id": 5
},
{
"id": 7,
"title": "Item 7",
"parent_id": 6
},
{
"id": 8,
"title": "Item 8",
"parent_id": 6
}
]
Desired Output:
[
{
"id": 1,
"title": "Item 1",
"parent_id": null,
"child": [{
"id": 2,
"title": "Item 2",
"parent_id": 1,
"child": [{
"id": 3,
"title": "Item 3",
"parent_id": 2
}]
}]
},
{
"id": 4,
"title": "Item 4",
"parent_id": null
},
{
"id": 5,
"title": "Item 5",
"parent_id": null,
"child": [{
"id": 6,
"title": "Item 6",
"parent_id": 5,
"child": [{
"id": 7,
"title": "Item 7",
"parent_id": 6
}, {
"id": 8,
"title": "Item 8",
"parent_id": 6
}]
}]
}
]
In the desired output I have created a nested array of object where each object will hold its children.
Can someone please suggest how we can do this in javascript?
Just take a loop and build a tree with the help of an object.
var data = [{ id: 1, title: "Item 1", parent_id: null }, { id: 2, title: "Item 2", parent_id: 1 }, { id: 3, title: "Item 3", parent_id: 2 }, { id: 4, title: "Item 4", parent_id: null }, { id: 5, title: "Item 5", parent_id: null }, { id: 6, title: "Item 6", parent_id: 5 }, { id: 7, title: "Item 7", parent_id: 6 }, { id: 8, title: "Item 8", parent_id: 6 }],
tree = function (data, root) {
var t = {};
data.forEach(o => {
Object.assign(t[o.id] = t[o.id] || {}, o);
t[o.parent_id] = t[o.parent_id] || {};
t[o.parent_id].children = t[o.parent_id].children || [];
t[o.parent_id].children.push(t[o.id]);
});
return t[root].children;
}(data, null);
console.log(tree);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Flatten nested array of objects prepending parent value to child

I have a nested array of objects and want to get only the child property elements of an array with the title of the parent element prepended to the title in the child. This is just an example and the actual data will include a unique children property in separate indices in the array.
I would like to implement flatMap in my solution instead of using flattenDeep and map from lodash. Please advice.
const headers = [{
"id": "name1",
"title": "Name 1",
"children": [{
"title": "Children 1",
"child": [{
"title": "Child 1",
"onClick": "child1Click"
}, {
"title": "Child 2",
"onClick": "child2Click"
}]
}, {
"title": "CHildren 2",
"child": [{
"title": "Child 3",
"id": "child3Click"
}, {
"title": "Child 4",
"id": "child4Click"
}]
}]
}, {
"id": "name2",
"title": "Name 2",
"children": [{
"title": "Children 3",
"child": [{
"title": "Child 5",
"onClick": "child5Click"
}, {
"title": "Child 6",
"onClick": "child6Click"
}]
}, {
"title": "CHildren 4",
"child": [{
"title": "Child 7",
"id": "child7Click"
}, {
"title": "Child 8",
"id": "child8Click"
}]
}]
}, {
"id": "name3",
"title": "Name 3"
}, {
"id": "name4",
"title": "Name 4"
}]
console.log(_.flattenDeep(_.map(_.flattenDeep(_.compact(_.map(headers, item => item.children))), item => _.map(item.child, child => {
return {
...child,
title: `${item.title} ${child.title}`
}
}))))
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.15/lodash.min.js"></script>
The output I expect is
[
{
"title": "Children 1 Child 1",
"onClick": "child1Click"
},
{
"title": "Children 1 Child 2",
"onClick": "child2Click"
},
{
"title": "CHildren 2 Child 3",
"id": "child3Click"
},
{
"title": "CHildren 2 Child 4",
"id": "child4Click"
},
{
"title": "Children 3 Child 5",
"onClick": "child5Click"
},
{
"title": "Children 3 Child 6",
"onClick": "child6Click"
},
{
"title": "CHildren 4 Child 7",
"id": "child7Click"
},
{
"title": "CHildren 4 Child 8",
"id": "child8Click"
}
]
Any help is greatly appreciated. Thanks
You can solve this with just reduce and two inner forEach loops for children and child.
const headers = [{"id":"name1","title":"Name 1","children":[{"title":"Children 1","child":[{"title":"Child 1","onClick":"child1Click"},{"title":"Child 2","onClick":"child2Click"}]},{"title":"CHildren 2","child":[{"title":"Child 3","id":"child3Click"},{"title":"Child 4","id":"child4Click"}]}]},{"id":"name2","title":"Name 2","children":[{"title":"Children 3","child":[{"title":"Child 5","onClick":"child5Click"},{"title":"Child 6","onClick":"child6Click"}]},{"title":"CHildren 4","child":[{"title":"Child 7","id":"child7Click"},{"title":"Child 8","id":"child8Click"}]}]},{"id":"name3","title":"Name 3"},{"id":"name4","title":"Name 4"}]
const result = headers.reduce((r, {children}) => {
children && children.forEach(({title: pTitle, child}) => {
child && child.forEach(({title, ...rest}) => r.push({
title: `${pTitle} ${title}`,
...rest
}))
})
return r;
}, [])
console.log(result)

Filtering array based on value in deeply nested object in javascript

I have array with following structure:
var topics = [
{
"id": 1,
"name": "topic title 1",
"sub_categories": [
{
"id": 1,
"name": "category title 1",
"indicators": [
{
"id": 1,
"name": "indicator 1",
"sub_category_id": 1
},
{
"id": 7,
"name": "indicator 7 - foo",
"sub_category_id": 1
}
]
},
{
"id": 6,
"name": "category title 6",
"indicators": [
{
"id": 8,
"name": "indicator 8",
"sub_category_id": 6
}
]
}
]
},
{
"id": 2,
"name": "topic title 2",
"sub_categories": [
{
"id": 2,
"name": "category 2",
"indicators": [
{
"id": 2,
"name": "indicator 2 - foo",
"sub_category_id": 2
}
]
},
{
"id": 4,
"name": "category 4",
"indicators": [
{
"id": 5,
"name": "indicator 5",
"sub_category_id": 4
}
]
}
]
}
];
I need to get filtered array based on value of name property in indicators array, removing non-matched indicators and both topic and sub_categories with empty indicators. So for input of foo, result would be:
var topics = [
{
"id": 1,
"name": "topic title 1",
"sub_categories": [
{
"id": 1,
"name": "category title 1",
"indicators": [
{
"id": 7,
"name": "indicator 7 - foo",
"sub_category_id": 1
}
]
}
]
},
{
"id": 2,
"name": "topic title 2",
"sub_categories": [
{
"id": 2,
"name": "category 2",
"indicators": [
{
"id": 2,
"name": "indicator 2 - foo",
"sub_category_id": 2
}
]
}
]
}
];
I tried to use lodash methods based on other similar SO question but all examples either have only one level of nesting or same keys on all levels (ie. children). I would be fine with either getting back new array or mutating existing one.
Here is an ES6 solution based on reduce, filter and Object.assign:
function filterTree(topics, find) {
return topics.reduce(function (acc, topic) {
const sub_categories = topic.sub_categories.reduce(function (acc, cat) {
const indicators = cat.indicators.filter( ind => ind.name.includes(find) );
return !indicators.length ? acc
: acc.concat(Object.assign({}, cat, { indicators }));
}, []);
return !sub_categories.length ? acc
: acc.concat(Object.assign({}, topic, { sub_categories }));
}, []);
}
// sample data
const topics = [
{
"id": 1,
"name": "topic title 1",
"sub_categories": [
{
"id": 1,
"name": "category title 1",
"indicators": [
{
"id": 1,
"name": "indicator 1",
"sub_category_id": 1
},
{
"id": 7,
"name": "indicator 7 - foo",
"sub_category_id": 1
}
]
},
{
"id": 6,
"name": "category title 6",
"indicators": [
{
"id": 8,
"name": "indicator 8",
"sub_category_id": 6
}
]
}
]
},
{
"id": 2,
"name": "topic title 2",
"sub_categories": [
{
"id": 2,
"name": "category 2",
"indicators": [
{
"id": 2,
"name": "indicator 2 - foo",
"sub_category_id": 2
}
]
},
{
"id": 4,
"name": "category 4",
"indicators": [
{
"id": 5,
"name": "indicator 5",
"sub_category_id": 4
}
]
}
]
}
];
// Call the function
var res = filterTree(topics, 'foo');
// Output result
console.log(res);
.as-console-wrapper { max-height: 100% !important; top: 0; }
You could use an iterative and recursive approach for filtering the given array, without hard wired properties.
const deepFilter = (array, indicator) => {
return array.filter(function iter(o) {
return Object.keys(o).some(k => {
if (typeof o[k] === 'string' && o[k].includes(indicator)) {
return true;
}
if (Array.isArray(o[k])) {
o[k] = o[k].filter(iter);
return o[k].length;
}
});
});
}
const topics = [{ id: 1, name: "topic title 1", sub_categories: [{ id: 1, name: "category title 1", indicators: [{ id: 1, name: "indicator 1", sub_category_id: 1 }, { id: 7, name: "indicator 7 - foo", sub_category_id: 1 }] }, { id: 6, name: "category title 6", indicators: [{ id: 8, name: "indicator 8", sub_category_id: 6 }] }] }, { id: 2, name: "topic title 2", sub_categories: [{ id: 2, name: "category 2", indicators: [{ id: 2, name: "indicator 2 - foo", sub_category_id: 2 }] }, { id: 4, name: "category 4", indicators: [{ id: 5, name: "indicator 5", sub_category_id: 4 }] }] }];
console.log(deepFilter(topics, 'foo'));
.as-console-wrapper { max-height: 100% !important; top: 0; }
This can pretty much all be done with ES 5 array methods (no library or polyfill needed for IE 9+):
var passed = topics.filter(function(x) {
return x.subcategories.some(function(y) {
return y.indicators.some(function(z) {
return Boolean(z.name.match(/foo/));
});
});
});
While this is total one-off code, the situation is perhaps too complicated for an easily digestible general-purpose solution (although I'd love to see someone prove me wrong).
UPDATE
After taking a closer look at the output you will need to use reduce instead of filter:
var passed = topics.reduce((acc, x) => {
var hasfoo = x.subcategories.reduce((accum, y) => {
var ls = y.indicators.filter(z => z.name.match(/foo/));
if (ls.length) {
accum.push(Object.assign({}, y, {indicators: ls}));
}
return accum;
}, []);
if (hasfoo.length) {
acc.push(Object.assign({}, x, {subcategories: hasfoo}));
}
return acc;
}, []);
Astute readers will note the recursive pattern here. Abstracting that out is left as an exercise, I'm tapped out. Object.assign will need to be polyfilled for old browsers (trivial though).
this will also modify existing topics
var result = topics.filter(top =>
(top.sub_categories = top.sub_categories.filter(cat =>
(cat.indicators = cat.indicators.filter(i => i.name.match(/foo/))).length)
).length
);
Example
var topics = [{
"id": 1,
"name": "topic title 1",
"sub_categories": [{
"id": 1,
"name": "category title 1",
"indicators": [{
"id": 1,
"name": "indicator 1",
"sub_category_id": 1
}, {
"id": 7,
"name": "indicator 7 - foo",
"sub_category_id": 1
}]
}, {
"id": 6,
"name": "category title 6",
"indicators": [{
"id": 8,
"name": "indicator 8",
"sub_category_id": 6
}]
}]
}, {
"id": 2,
"name": "topic title 2",
"sub_categories": [{
"id": 2,
"name": "category 2",
"indicators": [{
"id": 2,
"name": "indicator 2 - foo",
"sub_category_id": 2
}]
}, {
"id": 4,
"name": "category 4",
"indicators": [{
"id": 5,
"name": "indicator 5",
"sub_category_id": 4
}]
}]
}];
var result = topics.filter(top => (top.sub_categories = top.sub_categories.filter(cat => (cat.indicators = cat.indicators.filter(i => i.name.match(/foo/))).length)).length);
console.log(result);
One more implementation.
topics.forEach(function(topic, indexTopic, indexTopicArray) {
topic.sub_categories.forEach(function(subCat, indexsubCat, arraysubCat) {
subCat.indicators = subCat.indicators.filter(indic => indic.name.includes("foo"));
if(subCat.indicators.length === 0) {
indexTopicArray[indexTopic].sub_categories.splice(indexsubCat, 1);
}})});
console.log(topics);
Complete Code.
var topics = [
{
"id": 1,
"name": "topic title 1",
"sub_categories": [
{
"id": 1,
"name": "category title 1",
"indicators": [
{
"id": 1,
"name": "indicator 1",
"sub_category_id": 1
},
{
"id": 7,
"name": "indicator 7 - foo",
"sub_category_id": 1
}
]
},
{
"id": 6,
"name": "category title 6",
"indicators": [
{
"id": 8,
"name": "indicator 8",
"sub_category_id": 6
}
]
}
]
},
{
"id": 2,
"name": "topic title 2",
"sub_categories": [
{
"id": 2,
"name": "category 2",
"indicators": [
{
"id": 2,
"name": "indicator 2 - foo",
"sub_category_id": 2
}
]
},
{
"id": 4,
"name": "category 4",
"indicators": [
{
"id": 5,
"name": "indicator 5",
"sub_category_id": 4
}
]
}
]
}
];
topics.forEach(function(topic, indexTopic, indexTopicArray) {
topic.sub_categories.forEach(function(subCat, indexsubCat, arraysubCat) {
subCat.indicators = subCat.indicators.filter(indic => indic.name.includes("foo"));
if(subCat.indicators.length === 0) {
indexTopicArray[indexTopic].sub_categories.splice(indexsubCat, 1);
}})});
console.log(topics);
You can do it using _.filterDeep from deepdash extension for lodash:
var endsWith = 'foo';
var foundFoo = _.filterDeep(
obj,
function(value, key) {
return _.endsWith(value.name, endsWith);
},
{ tree: { children: ['sub_categories', 'indicators'] } }
);
Here is a full test for your case

Categories