Save the parent to build a tree array - javascript

I'm struggling to manipulating a tree object in JS, it might be simple but I can't find out.
Here is the object :
{
"100-silk": {
"url": "https://www.striiiipes.com/product-category/100-silk/"
},
"apparel": {
"url": "https://www.striiiipes.com/product-category/apparel/"
},
"ipad-accessories": {
"url": "https://www.striiiipes.com/product-category/ipad-accessories/",
"ipad-air": {
"url": "https://www.striiiipes.com/product-category/ipad-accessories/ipad-air/"
},
"ipad-mini": {
"url": "https://www.striiiipes.com/product-category/ipad-accessories/ipad-mini/"
},
"ipad-pro": {
"url": "https://www.striiiipes.com/product-category/ipad-accessories/ipad-pro/",
"ipad-pro-12-9": {
"url": "https://www.striiiipes.com/product-category/ipad-accessories/ipad-pro/ipad-pro-12-9/"
},
"ipad-pro-9-7": {
"url": "https://www.striiiipes.com/product-category/ipad-accessories/ipad-pro/ipad-pro-9-7/"
}
}
}
I'm using a simple recursion function to get throught all childrens :
function parseCategories(a) {
if (typeof a === 'object') {
$.each(a, function (i, v) {
//if we only have a "url" object, then we're on last branch and can parse it
if (Object.keys(v).length === 1 && 'url' in v) {
//dosomething
} else { // then we need to go deeper in the tree
parseCategories(v);
}
})
}
}
I'd like to use this function to also save the breadcrumb and get an object like this :
"100-silk": {
"categories" : ["100-silk"],
"url": "https://www.striiiipes.com/product-category/100-silk/"
},
"apparel": {
"categories" : ["apparel"],
"url": "https://www.striiiipes.com/product-category/apparel/"
},
"ipad-accessories": {
"categories" : ["ipad-accessories"],
"url": "https://www.striiiipes.com/product-category/ipad-accessories/",
"ipad-air": {
"categories" : ["ipad-accessories", "ipad-air"],
"url": "https://www.striiiipes.com/product-category/ipad-accessories/ipad-air/"
},
"ipad-mini": {
"categories" : ["ipad-accessories", "ipad-mini"],
"url": "https://www.striiiipes.com/product-category/ipad-accessories/ipad-mini/"
},
"ipad-pro": {
"categories" : ["ipad-accessories", "ipad-pro"],
"url": "https://www.striiiipes.com/product-category/ipad-accessories/ipad-pro/",
"ipad-pro-12-9": {
"categories" : ["ipad-accessories", "ipad-pro", "ipad-pro-12-9"],
"url": "https://www.striiiipes.com/product-category/ipad-accessories/ipad-pro/ipad-pro-12-9/"
},
"ipad-pro-9-7": {
"categories" : ["ipad-accessories", "ipad-pro", "ipad-pro-9-7"],
"url": "https://www.striiiipes.com/product-category/ipad-accessories/ipad-pro/ipad-pro-9-7/"
}
}
}
};
So the point is to save the full breadcrumb in each child and parent. How should I change the parsing function to get this object ? I've tried to pass also the parent in each loop but I can't manage to get exactly what I want...
I've create a jsfiddle here.
Thanks a lot for your help !

You could check the properties for nested objects and assign the category value.
It works with a check, if
object[k] is a truthy value, and
typeof object[k] === 'object', if the type is an object, and
!Array.isArray(object[k]), if the actual property is not an array, like a new added category,
then it assigns a new property category by using the category from the level before and adding the actual key to it.
function updateCategory(object) {
Object.keys(object).forEach(function (k) {
if (object[k] && typeof object[k] === 'object' && !Array.isArray(object[k])) {
object[k].category = (object.category || []).concat(k);
updateCategory(object[k]);
}
});
}
var data = { "100-silk": { url: "URL/100-silk/" }, apparel: { url: "URL/apparel/" }, "ipad-accessories": { url: "URL/ipad-accessories/", "ipad-air": { url: "URL/ipad-accessories/ipad-air/" }, "ipad-mini": { url: "URL/ipad-accessories/ipad-mini/" }, "ipad-pro": { url: "URL/ipad-accessories/ipad-pro/", "ipad-pro-12-9": { url: "URL/ipad-accessories/ipad-pro/ipad-pro-12-9/" }, "ipad-pro-9-7": { url: "URL/ipad-accessories/ipad-pro/ipad-pro-9-7/" } } } };
updateCategory(data);
console.log(data);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Related

Convert a list of objects with paths to a nested hierarchy structure

This is my input:
[
{
"id":360006927051,
"name":"KBVSA::Agent",
"raw_name":"KBVSA::Agent",
"value":"kbvsa__agent",
"default":false
},
{
"id":360006927071,
"name":"KBVSA::Agent Procedure",
"raw_name":"VSA::Agent Procedure",
"value":"vsa__agent_procedure",
"default":false
},
{
"id":360006927091,
"name":"KBVSA::Anti-Malware",
"raw_name":"KBVSA::Anti-Malware",
"value":"kbvsa__anti-malware",
"default":false
},
{
"id":360006927111,
"name":"KMTraverse::Devices",
"raw_name":"KMTraverse::Devices",
"value":"kmtraverse__devices",
"default":false
},
{
"id":360006927131,
"name":"KMTraverse::Signatures",
"raw_name":"KMTraverse::Signatures",
"value":"kmtraverse_signatures",
"default":false
},
{
"id":360006927151,
"name":"MCBMS::HR",
"raw_name":"MCBMS::HR",
"value":"mcbms__hr",
"default":false
},
{
"id":360006927171,
"name":"MCBMS::Finance",
"raw_name":"MCBMS::Finance",
"value":"mcbms__finance",
"default":false
},
{
"id":360006927191,
"name":"TMONAuthAnvil::On-Demand::2FA",
"raw_name":"TMONAuthAnvil::On-Demand::2FA",
"value":"tmonauthanvil__on-demand__2fa",
"default":false
},
{
"id":360006927211,
"name":"TMONAuthAnvil::On-Premise::2fa",
"raw_name":"TMONAuthAnvil::On-Premise::2fa",
"value":"tmonauthanvil__on-premise__2fa",
"default":false
},
{
"id":360006927231,
"name":"KYUnigma::Monitor",
"raw_name":"kyUnigma::Monitor",
"value":"kyunigma__monitor",
"default":false
},
{
"id":360006927251,
"name":"KYUnigma::Report",
"raw_name":"kyUnigma::Report",
"value":"kyunigma__report",
"default":false
},
{
"id":360006927271,
"name":"2365 Command::Monitor",
"raw_name":"365 Command::Monitor",
"value":"365_command__monitor",
"default":false
},
{
"id":360006927291,
"name":"2365 Command::Report",
"raw_name":"2365 Command::Report",
"value":"2365_command__report",
"default":false
}
]
This is the output I am trying to get from it:
[
{
"label":"-",
"value":""
},
{
"label":"KBVSA",
"options":[
{
"label":"Agent",
"value":"kbvsa__agent"
},
{
"label":"Agent Procedure",
"value":"vsa__agent_procedure"
},
{
"label":"Anti-Malware",
"value":"vsa__anti-malware"
}
]
},
{
"label":"KMTraverse",
"options":[
{
"label":"Devices",
"value":"kmtraverse__devices"
},
{
"label":"Signatures",
"value":"kmtraverse_signatures"
}
]
},
{
"label":"MCBMS",
"options":[
{
"label":"HR",
"value":"mcbms__hr"
},
{
"label":"Finance",
"value":"mcbms__finance"
}
]
},
{
"label":"TMONAuthAnvil",
"options":[
{
"label":"On-Demand",
"options":[
{
"label":"2FA",
"value":"tmonauthanvil__on-demand__2fa"
}
]
},
{
"label":"On-Premise",
"options":[
{
"label":"2fa",
"value":"tmonauthanvil__on-premise__2fa"
}
]
}
]
},
{
"label":"KYUnigma",
"options":[
{
"label":"Monitor",
"value":"kyunigma__monitor"
},
{
"label":"Report",
"value":"kyunigma__report"
}
]
},
{
"label":"23365 Command",
"options":[
{
"label":"Monitor",
"value":"365_command__monitor"
},
{
"label":"Report",
"value":"365_command__report"
}
]
}
]
I'm trying to create a new array and push it to data like this. But I'm not able to make the exact output Array Name split ("::"), and distinguish between the first name(s), that is/are common, and the new label's value as sub arrays.
I'm trying to make a select option design where a single select will show multiple related values.
You could create a Map keyed by "path" (i.e. a "::"-delimited string), and map each key to a target node you have created for that key.
As you iterate the labels that make up a path, look up the corresponding partial path in that map, and if it doesn't exist yet, create the target object for it (with a label property), and include it in the parent's options property. That parent is the node that was retrieved/created in the previous iteration of this path traversal.
When the iteration of a path is complete, attach the value property to the last node that was created in that traversal.
As the entry { label: "-", value: "" } has no backing source in your input data, I just hard-coded it as an initialisation step. I didn't get clarification when I asked about it.
Here is an implementation:
function convert(data) {
const map = new Map;
const root = { options: [{ label: "-", value: "" }] };
for (const {name, value} of data) {
let path = "";
let parent = root;
for (const label of name.split("::")) {
path += "::" + label;
let node = map.get(path);
if (!node) {
map.set(path, node = { label });
(parent.options ??= []).push(node);
}
parent = node;
}
parent.value = value;
}
return root.options;
}
const data = [{"id":360006927051,"name":"KBVSA::Agent","raw_name":"KBVSA::Agent","value":"kbvsa__agent","default":false},{"id":360006927071,"name":"KBVSA::Agent Procedure","raw_name":"VSA::Agent Procedure","value":"vsa__agent_procedure","default":false},{"id":360006927091,"name":"KBVSA::Anti-Malware","raw_name":"KBVSA::Anti-Malware","value":"kbvsa__anti-malware","default":false},{"id":360006927111,"name":"KMTraverse::Devices","raw_name":"KMTraverse::Devices","value":"kmtraverse__devices","default":false},{"id":360006927131,"name":"KMTraverse::Signatures","raw_name":"KMTraverse::Signatures","value":"kmtraverse_signatures","default":false},{"id":360006927151,"name":"MCBMS::HR","raw_name":"MCBMS::HR","value":"mcbms__hr","default":false},{"id":360006927171,"name":"MCBMS::Finance","raw_name":"MCBMS::Finance","value":"mcbms__finance","default":false},{"id":360006927191,"name":"TMONAuthAnvil::On-Demand::2FA","raw_name":"TMONAuthAnvil::On-Demand::2FA","value":"tmonauthanvil__on-demand__2fa","default":false},{"id":360006927211,"name":"TMONAuthAnvil::On-Premise::2fa","raw_name":"TMONAuthAnvil::On-Premise::2fa","value":"tmonauthanvil__on-premise__2fa","default":false},{"id":360006927231,"name":"KYUnigma::Monitor","raw_name":"kyUnigma::Monitor","value":"kyunigma__monitor","default":false},{"id":360006927251,"name":"KYUnigma::Report","raw_name":"kyUnigma::Report","value":"kyunigma__report","default":false},{"id":360006927271,"name":"2365 Command::Monitor","raw_name":"365 Command::Monitor","value":"365_command__monitor","default":false},{"id":360006927291,"name":"2365 Command::Report","raw_name":"2365 Command::Report","value":"2365_command__report","default":false}];
const result = convert(data);
console.log(result);

Computed property throws an error although working

I have a this code that fetches data from a remote server and i have placed it on mounted
var get_one = 'https://api.example.com/rb.php';
axios.get(get_one)
.then(response => {
// console.log(response.data);
var data = response.data;
this.roomsData = data;
this.room_name = response.data.room_name;
this.roomsData.room_photos = response.data.room_photos;
})
.catch(e => {
this.errors.push(e)
});
when i refresh the page and {{roomsData.room_photos}} looks like this
[ { "url": "/var/www/html/uploads/609cd3aaaaefd.jpg" }, { "url": "/var/www/html/uploads/609cd3aaa9dc2.jpg" }, { "url": "/var/www/html/uploads/609cd3aab1252.jpg" }, { "url": "/var/www/html/uploads/609cd3aab147a.jpg" }, { "url": "/var/www/html/uploads/609cd3aab0226.jpg" }, { "url": "/var/www/html/uploads/609cd3aaaf92b.jpg" }, { "url": "/var/www/html/uploads/609cd3aaec480.jpg" }, { "url": "/var/www/html/uploads/609cd3ab3a61b.jpg" }, { "url": "/var/www/html/uploads/609cd3ab432cb.jpg" }, { "url": "/var/www/html/uploads/609cd3ab00b91.jpg" }, { "url": "/var/www/html/uploads/609cd3ab02040.jpg" }, { "url": "/var/www/html/uploads/609cd3ab43f3e.jpg" }, { "url": "/var/www/html/uploads/609cd3ab3a634.jpg" }, { "url": "/var/www/html/uploads/609cd3ab4729f.jpg" }, { "url": "/var/www/html/uploads/609cd3ab47168.jpg" }, { "url": "/var/www/html/uploads/609cd3ab7af65.jpg" }, { "url": "/var/www/html/uploads/609cd3ab7dae1.jpg" }, { "url": "/var/www/html/uploads/609cd3ab8738f.jpg" }, { "url": "/var/www/html/uploads/609cd3ab86f15.jpg" }, { "url": "/var/www/html/uploads/609cd3ab8af48.jpg" }, { "url": "/var/www/html/uploads/609cd3ab95423.jpg" }, { "url": "/var/www/html/uploads/609cd3abbbdf9.jpg" }, { "url": "/var/www/html/uploads/609cd3abc455e.jpg" }, { "url": "/var/www/html/uploads/609cd3abca83e.jpg" }, { "url": "/var/www/html/uploads/609cd3abca0a0.jpg" }, { "url": "/var/www/html/uploads/609cd3abcfa7a.jpg" }, { "url": "/var/www/html/uploads/609cd3abd9d73.jpg" }, { "url": "/var/www/html/uploads/609cd3ac09a00.jpg" }, { "url": "/var/www/html/uploads/609cd3ac1382f.jpg" } ]
This is my computed function
computed: {
image_viewer_data: function () {
let vd = this.roomsData.room_photos
console.log('inside computed',vd)
let modifiedArr = vd.map(function(item){
let url_parts = item.url.split('/')
return 'https://api.example.com/uploads' + '/' + url_parts[url_parts.length - 1]
});
return modifiedArr;
}
},
which produces data in the following format when i {{image_viewer_data}}
[ "https://api.example.com/uploads/609cd3aaaaefd.jpg", "https://api.example.com/uploads/609cd3aaa9dc2.jpg", "https://api.example.com/uploads/609cd3aab1252.jpg", "https://api.example.com/uploads/609cd3aab147a.jpg", "https://api.example.com/uploads/609cd3aab0226.jpg", "https://api.example.com/uploads/609cd3aaaf92b.jpg", "https://api.example.com/uploads/609cd3aaec480.jpg", "https://api.example.com/uploads/609cd3ab3a61b.jpg", "https://api.example.com/uploads/609cd3ab432cb.jpg", "https://api.example.com/uploads/609cd3ab00b91.jpg", "https://api.example.com/uploads/609cd3ab02040.jpg", "https://api.example.com/uploads/609cd3ab43f3e.jpg", "https://api.example.com/uploads/609cd3ab3a634.jpg", "https://api.example.com/uploads/609cd3ab4729f.jpg", "https://api.example.com/uploads/609cd3ab47168.jpg", "https://api.example.com/uploads/609cd3ab7af65.jpg", "https://api.example.com/uploads/609cd3ab7dae1.jpg", "https://api.example.com/uploads/609cd3ab8738f.jpg", "https://api.example.com/uploads/609cd3ab86f15.jpg", "https://api.example.com/uploads/609cd3ab8af48.jpg", "https://api.example.com/uploads/609cd3ab95423.jpg", "https://api.example.com/uploads/609cd3abbbdf9.jpg", "https://api.example.com/uploads/609cd3abc455e.jpg", "https://api.example.com/uploads/609cd3abca83e.jpg", "https://api.example.com/uploads/609cd3abca0a0.jpg", "https://api.example.com/uploads/609cd3abcfa7a.jpg", "https://api.example.com/uploads/609cd3abd9d73.jpg", "https://api.example.com/uploads/609cd3ac09a00.jpg", "https://api.example.com/uploads/609cd3ac1382f.jpg" ]
This is how i am using my computed function
<div class="carousel-item" v-for="(value,index) in image_viewer_data" >
<img class="d-block w-100" :src="value" :id="index" >
</div>
when i refresh the page i get this errors.
[Vue warn]: Error in render: "TypeError: vd.map is not a function
and
TypeError: vd.map is not a function
Why is the code simultaneously working and failing at the same time.
that warning message means that vd is not an array , thus cannot find map() . and its working because the type of object you have is iterable so it behaved like an array.
try creating a new array instance from it using Array.from() :
let vd = [ { "url": "/var/www/html/uploads/609cd3aaaaefd.jpg" }, { "url": "/var/www/html/uploads/609cd3aaa9dc2.jpg" }, { "url": "/var/www/html/uploads/609cd3aab1252.jpg" }, { "url": "/var/www/html/uploads/609cd3aab147a.jpg" } ]
let modifiedArr = Array.from(vd).map(function(item){
let url_parts = item.url.split('/');
return 'https://api.example.com/uploads' + '/' + url_parts[url_parts.length - 1];
});
console.log(modifiedArr);
seems like the vd is not an array while you getting errors. Wondering why? The possible reason, your code is accessing the vd.map before the data loads.
So, adding a null check before accessing vd will help here. Eg:
let modifiedArr = vd?.map(item=>{
...
...
}) || [];
will be a perfect blend here.
In common format,
let modifiedArr = vd && vd.map() || []

Select2 optgroup format using PHP

I've got an ajax call that get dynamic data and places them in select2 as follow :
$.ajax({
type: 'get',
url: '/api/?stuff='+c,
dataType: "json",
success: function (response) {
// If select2 is already defined, we destroy it and rebuild it with the new data
if(typeof $(".select2edit").data('select2') !== 'undefined') {
$(".select2edit").select2('destroy').select2({ data: response, width: '100%', closeOnSelect: false });
} else {
$(".select2edit").select2({ data: response, width: '100%', closeOnSelect: false });
}
}
});
I create the response using PHP and then transform it to JSON before sending it :
$old_emplacement = '';
$results = array();
$i = -1;
while($array_campaign = tep_db_fetch_array($campaign)){
if ($array_campaign['name'] != $old_emplacement) {
$i++;
$results['results'][$i]['text'] = $array_campaign['name'];
$old_emplacement = $array_campaign['name'];
$c = 0;
}
$results['results'][$i]['children'][$c]['id'] = $array_campaign['id'];
$results['results'][$i]['children'][$c]['text'] = $array_campaign['c_name'];
$c++;
}
$results['pagination']["more"] = true;
Thus resulting in the following JSON format :
{
"results": [
{
"text": "Name 1",
"children" : [
{
"id": 1,
"text": "Text 1.1"
},
{
"id": 2,
"text": "Text 1.2"
}
]
},
{
"text": "Name 2",
"children" : [
{
"id": 1,
"text": "Text 2.1"
},
{
"id": 2,
"text": "Text 2.2"
}
]
}
],
"paginate": {
"more": true
}
}
I get a No results found. when select2 initializes and loads. And I have no idea why. It's the correct format as far as the documentation is saying and other questions seem to confirm. Any ideas where could the problem be coming from?
It's also good to note that my select2 is inside a form which is inside a modal, and this is it's html :
<select name="xx[]" id="edit-xx" name='xx' class="form-control select2edit" multiple>
</select>
The problem was with the format generated by my PHP code. I'm posting the result here for anyone trying to generate a select2 optgroup format using PHP and for my own reference :
$old_emplacement = '';
$results = array();
$i = -1;
while($array_campaign = tep_db_fetch_array($campaign)){
if ($array_campaign['name'] != $old_emplacement) {
$i++;
$results[$i]['text'] = $array_campaign['name'];
$old_emplacement = $array_campaign['name'];
$c = 0;
}
$results[$i]['children'][$c]['id'] = $array_campaign['id'];
$results[$i]['children'][$c]['text'] = $array_campaign['c_name'];
if(in_array($array_campaign['id'], $campaigns_array)) {
$results[$i]['children'][$c]['selected'] = true;
}
$c++;
}

jsTree Contextmenu get selected node id

Hi I am currently having an issue with the jsTree Context menu.
I would like to grab the id of the selected node however I've looked at a number of solutions and none have worked. My code is below.
The function I have which uses tree.get_selected($node) does return an object of the selected node but I'm struggling to access the id parameter of the object. Below I used tree.get_selected($node).id but it says it's undefined but its clearly not when I look at the console.
Can anyone help as to where I'm going wrong?
$("#tree_1").jstree({
"core" : {
"themes" : {
"responsive": false
},
// so that create works
"check_callback" : true,
'data' : {
'url' : function (node) {
if(node.id=='#') {
var id = 0;
} else {
var id = node.id;
}
return 'organisation/getOrganisationLocations/'+id;
}
}
},
"types" : {
"default" : {
"icon" : "fa fa-folder icon-state-warning icon-lg"
},
"file" : {
"icon" : "fa fa-file icon-state-warning icon-lg"
}
},
"state" : { "key" : "demo3" },
"plugins" : [ "dnd", "state", "types", "contextmenu" ],
"contextmenu":{
"items": function($node) {
var tree = $("#tree_1").jstree(true);
return {
"Create": {
"separator_before": false,
"separator_after": false,
"label": "Create",
"action": function (obj) {
$node = tree.create_node($node);
tree.edit($node);
console.log(tree.get_selected($node).id);
}
},
"Rename": {
"separator_before": false,
"separator_after": false,
"label": "Rename",
"action": function (obj) {
tree.edit($node);
}
},
"Remove": {
"separator_before": false,
"separator_after": false,
"label": "Remove",
"action": function (obj) {
tree.delete_node($node);
}
}
};
}
}
});
This approach worked for me:
$(node).attr('id')
Declare your id above the jstree declaration (at line 1, put var id;)
Then, in the data.url function, remove var from before each line that assignes to id.

jstree select_limit not working. I want to set selection limit to select only 3 nodes

My jstree function is here.
I have set 'select_limit' : 3, but is not working. when I run, I am able to select more than 3 nodes, but I need to select no more than 3 nodes.
var j1 = jQuery.noConflict();
j1("#utree_activity").jstree({
"plugins": ["themes", "html_data", "ui", "crrm", "checkbox"],
"html_data": {
"ajax": {
"url": urlGlobal + "jstrees/activitytree/",
"asynchronous": "false",
"data": function (n) {
return {
id: n.attr ? n.attr("id") : 0,
default_activities: default_activities
};
},
"success": function (gb) {
},
}
},
"ui": {
"select_limit": 3,
},
"cookies": {
cookie_options: {
path: "/"
}
},
"checkbox": {
two_state: true,
real_checkboxes: false
}
});
select_limit doens't handle checkbox you must roll your own before.jstree method.
j1.bind("before.jstree", function (e, data) {
if (data.func === "check_node") {
if (j1.jstree('get_checked').length >= 1) {
e.preventDefault();
return false;
}
}
});
Note that this code if for example only, and doesn't handle child nodes
Working fiddle: http://jsfiddle.net/cfb9J/1/
There is another option missing, probably need to add the ui module, try this:
j1("#utree_activity").jstree({
"plugins" : ["html_data","ui"],
//the rest of your code
});

Categories