d3.js tree layout with array as data structure - javascript

I want to create a diagram with d3.js using the tree layout. Instead of the common flare json structure with hierarchical children like:
{
"name":"bla",
"children":[
{
"name":"bla2",
"someattribute":"green"
}
]
}
I just have an array (of elements which represent a timestep). I always want the next element(s) of the array to be the child(ren) element of the current.
So in fact I want a tree to be generated out of a flattened array.
My idea was to change the children function of the tree layout like this:
var tree = d3.layout.tree()
.children(function(d,i) {
return data[(i+1)];
})
But only one element is displayed in the tree. I don't get it. Shouldn't it iterate over every element when calling tree.nodes on data[0] ?
To clarify my request: I want to turn an array like [ele1, ele2, ele3] into a tree graph that looks like:
ele1-->ele2-->ele3

The real issue I see is that the function passed to .children() needs to return an Array of children Objects, instead of a single Object as it is now, i.e.
var tree = d3.layout.tree()
.children(function(d,i) {
return i < data.length - 1; data.slice(i+1, i+2) : null;
})
It's also important that the leaf node (the last data Object in the array) returns null instead of an empty list.
Using this approach, I created a working JSFiddle that will create a linear tree from an array of objects.
However, I agree with #LarsKotthoff's comment on your post; your code would be better served in terms maintainability and flexibility if you passed tree.nodes() a root of an Object in hierarchical format.

Related

Vue Tree flatten array or recursive component

Hi guys I'm trying to build Tree component in Vue, I'm having a little doubt now I have already built a recursive component, similar to this one, but more complex with checkboxes, drag drop etc
https://v2.vuejs.org/v2/examples/tree-view.html
But now I'm looking at some samples online and it looks of them are made by flattening nested json and making a Tree out of it
Like this one:
https://jsfiddle.net/fernando2684/p0k8szvj/43/
Hi here recursive builds array and then builds Tree out of it
recursive(obj, newObj, level, itemId, isExpend) {
let vm = this;
obj.forEach(function(o) {
if (o.children && o.children.length != 0) {
o.level = level;
o.leaf = false;
newObj.push(o);
if (o.id == itemId) {
o.expend = isExpend;
}
if (o.expend == true) {
vm.recursive(o.children, newObj, o.level + 1, itemId, isExpend);
}
} else {
o.level = level;
o.leaf = true;
newObj.push(o);
return false;
}
});
},
Could someone tell, what could be real benefit out of this, I see it could be easier to maintain and all the data in array is reactive since it is only in one level ???
It's a matter of optimizing the data for use in the template.
Flat array
If you're rendering a table, using recursive components will be difficult to implement.
If you have an array, you can pass it through a v-for and you end up with a single component that has all the children (no matter the depth) as direct children. This makes controlling actions easier.
Nested Object
But if you want to use divs that recursively indent, then using a flat array is more difficult.
If you were to use objects, you would have to use recursive components with their individual parent-child relationships that need to bubble up.
Do which ever suits you better, but don't think optimizing data for template is worse than creating more complex component relationships.

javascript - recursively removing flagged elements from multidimensional array

I have an array of objects, which contains more array of objects (with the same structure) with unknown depth.
sTree = [{
Tree: [{
Tree: [{
}],
Leafs:[{},{},{}]
}],
Leafs:[{},{},{}]
}
it's a classic (and actual) tree.
Each Object has a reference in a DOM object (using $(obj).data("ref",obj)).
|this part is done|
The UI is flagging some of the objects with obj.deleted = true.
|this part is done|
When the user is done, i want to get back the sTree, without the deleted=true flagged items.
How can it be done?
thanks
Do it with recursion. Loop over the structure and check every item like this:
function cleanTree(tree){
for(var i in tree){
if(tree[i].deleted){
// debug output
console.log('delete '+tree[i].toString());
delete tree[i];
}else{
// debug output
console.log('look at '+tree[i].toString());
tree[i] = cleanTree(tree[i]);
}
}
return tree;
}
You have to change the inside of the for-loop a bit to work with your structure.

Parse Cloud Query array contains value

I have a parse data model like...
Parent
------
children - Array of pointers to Child objects
I'm trying to create a query that says...
Find all Parents where the children array contains the specific Child object.
It's sort of the opposite of this function from the docs.
query.containedIn("playerName", ["Jonathan Walsh", "Dario Wunsch", "Shawn Simon"]);
// note you can also do this with object pointers which is EXACTLY opposite to what I want.
This will find all the players where the playerName is contained in the given array.
I want this but I want to give a value and that value to be in the array for the key.
I imagine it's something like...
query.contains("children", someChildObject);
but the docs for contains shows it only works for substrings of strings.
How would I do what I'm looking for?
You should use query.equalTo for key with an array type.
Try query like following:
var ChildClass = Parse.Object.extend('ChildClass');
var childObj = new ChildClass();
childObj.id = 'objId';
// you don't need to do above if you already have the object.
query.equalTo("children", childObj);
...
ref. Queries on Array Values

How can I add elements to a part of a json by using jquery

I try to add elements in a particular way to the following JSON:
var data = [{"name":"google",
"ip":"10.10.10.01",
"markets":[{"name":"spain","county":"6002,6017,6018,6019,6020"},
{"name":"france","county":"6003,6005,6006,6007,6008,6025,6026,6027,6028,6029"},
{"name":"japan","county":"6004,6021,6022,6023,6024"},
{"name":"korea","county":"6000,6013,6014,6015,6016"},
{"name":"vietnam","county":"6001,6009,6010,6011,6012"}]},
{"name":"amazon",
"ip":"10.10.10.02",
"markets":[{"name":"usa","county":"10000,10001,10002,10003,10004,10005"}]},
{"name":"yahoo",
"ip":"10.10.10.03",
"markets":[{"name":"japan","county":"10000"}]}];
I want to add this element to the json:
newData = [{"name":"amazon",
"ip":"10.10.10.02",
"markets":[{"name":"mexico","county":"9000"}]}];
The result might be exactly this:
var data = [{"name":"google",
"ip":"10.10.10.01",
"markets":[{"name":"spain","county":"6002,6017,6018,6019,6020"},
{"name":"france","county":"6003,6005,6006,6007,6008,6025,6026,6027,6028,6029"},
{"name":"japan","county":"6004,6021,6022,6023,6024"},
{"name":"korea","county":"6000,6013,6014,6015,6016"},
{"name":"vietnam","county":"6001,6009,6010,6011,6012"}]},
{"name":"amazon",
"ip":"10.10.10.02",
"markets":[{"name":"usa","county":"10000,10001,10002,10003,10004,10005"},
{"name":"mexico","county":"9000"}]},
{"name":"yahoo",
"ip":"10.10.10.03",
"markets":[{"name":"japan","county":"10000"}]}];
I tried to use :
$.extend(data.markets, newData)
$.extend(true, data, newData); //this works only in the case every element is new.
but nothing works the way I pretend.
Could anyone give me a solution?
Thanks in advance.
You haven't created JSON, you've created a JavaScript literal object.
You could add this particular piece of newdata by
data[1].markets.push({"name":"mexico","county":"9000"})
Because you are dealing with javascript objects, you can write a function to check for the existence of data[n] and push data.
You have an array of objects, where each object is like the following:
var item = {"name":"...",
"ip":"...",
"markets":[ /*some objects here*/];
}
So why not just creating your custom method to insert elements? It could search in the array if an item with the same name and ip exists, and then:
If it does exist: append the markets to the existing item markets attribute (maybe you need to check again if they already exist). UPDATE:The code that #jasonscript added in his answer will do the job: once you have found where to add the market, just add it to the array. Again, maybe you'll have to check if that market was already in the array. Using jQuery it will be: $.extend(true, data[i],newData)
If it doesn't exist: just add the item to the array: $.extend(true, data,newData)
Stealing a little code from another answer:
$.each(data, function(item){
if(item.name == newData[0].name && item.ip == newData[0].ip) {
item.markets.push.apply(item.markets, newData[0].markets);
}
}
This assumes that you know that all the market items in the new object are different to the existing ones - otherwise you'd have to do a nested foreach or something. If you can change the notation of the objects a little you could think about using a dictionary-like object for Markets to make that a little cleaner.
In fact, changing data from an associative array would probably work for that too. Then you could easily check for existence with:
if(data[myNewDataName]){
//add to markets
} else {
data[myNewDataName] = myNewData;
}

JavaScript insertAt position

Assuming I have a collection with a bunch of well ordered elements, what's the common method to insert another new child at it's abstract order-position?
Using a dom library $(new).eq($(new).data('orderPosition')); doesn't work, because it's not a valid index.
// Add this element to it's logic position in the collection:
<div data-order-position="10"></div>
// The collection
<div id="myCollection">
<div data-order-position="4"></div>
<div data-order-position="67"></div>
<div data-order-position="81"></div>
<div data-order-position="82"></div>
<div data-order-position="761"></div>
</div>
My real collection contains about ~400 elements.
I think that working with an array of integers is probably the most efficient method. You can maintain a constant list of the sorted elements in an array somewhere (and even continue to sort as needed):
//Array of positions
var positions = [];
//Initially set up the array in question
//divs are already sorted, as we know
$("#myCollection div").each(function () {
positions.push(parseInt(this.dataset.orderPosition));
});
//Create the new node we want to insert (in your case it may already exist)
var $new = $("<div>").data('order-position', 10).text(10);
//Append the new node index (if node already exists, just use `.data` as above)
positions.push(10);
//Yes, for whatever reason JS sorts by string and not number by default.
//There may also be a more efficient way to add the integer in the correct spot
//without having to sort all over again, but this is fast enough
positions.sort(function (a, b) {
return a - b;
});
//Insert the new node!
$("#myCollection div").eq(positions.indexOf(10) - 1).after($new);
http://jsfiddle.net/ExplosionPIlls/ULEFX/
why don't you just store the order-position in an array and then calculate the index using it? it is far better solution as reading DOM property consumes a lot more CPU than just loop through array and compare your new item with existing ones

Categories

Resources