How can I convert a tabbed tree to JSON in JavaScript? - javascript

I've looked around for an answer, but I think this is a kind of weird question. How would I convert, as a text file using tabs for spacing, this:
parent
child
child
parent
child
grandchild
grandhcild
to
{
"name" : "parent",
"children" : [
{"name" : "child"},
{"name" : "child"},
]
},
{
"name" : "parent",
"children" : [
{
"name" : "child",
"children" : [
{"name" : "grandchild"},
{"name" : "grandchild"},
{"name" : "grandchild"},
]
},
]
}
JSON probably isn't perfect, but hopefully makes my point clear.

i've had the same problem. Here is the solution:
function node(title,lvl){
var children = [],
parent = null;
return {
title:title,
children:children,
lvl:()=>lvl==undefined?-1:lvl,
parent:()=>parent, //as a function to prevent circular reference when parse to JSON
setParent:p=>{parent=p},
appendChildren: function(c){
children.push(c);
c.setParent(this);
return this
},
}
}
function append_rec(prev,curr) {
if(typeof(curr)=='string'){ //in the recursive call it's a object
curr = curr.split(' ');//or tab (\t)
curr = node(curr.pop(),curr.length);
}
if(curr.lvl()>prev.lvl()){//curr is prev's child
prev.appendChildren(curr);
}else if(curr.lvl()<prev.lvl()){
append_rec(prev.parent(),curr) //recursive call to find the right parent level
}else{//curr is prev's sibling
prev.parent().appendChildren(curr);
}
return curr;
}
root = node('root');
var txt =
`parent
child
child
parent
child
grandchild
grandhcild`;
txt.toString().split('\n').reduce(append_rec,root);
console.log(JSON.stringify(root.children,null,3));

I've just implemented this feature for the tabdown markup language — it does exactly what you sought for.
https://github.com/antyakushev/tabdown
Usage is pretty simple:
var lines = data.toString().split('\n');
var tree = tabdown.parse(lines);
console.log(tree.toString());
You can also use the parse function outside of node.js, it does not depend on any modules.

This is my regex-based, recursion-free approach. It looks a bit "hacky" but makes perfect sense, you can try each step on regexr if you want to. It's written purposely verbose and can probably be compressed a bit. Also, this code assumes your text is tab-indented and only has one "parent", but you should be able to easily replace your indents and add a single "root" parent beforehand.
const string = `
parent
child
grandchild
child
child
grandchild
grandchild
`;
let json = string
.replace(
/(?:(\t+)(\S+)(?=(?:\n(?:(?:(?!\1))|(?:\1\S)))|$))/g,
"$1{\n$1\t\"name\": \"$2\",\n$1\t\"children\": []\n$1},"
) // this one replaces all empty nodes with a simple object with an empty children array
.replace(
/(?<=(^\t*))([^\s{]+)$\n(?=\1\t)/gm,
"{\"name\": \"$2\",\"children\": [\n"
); // this one replaces every immediate parent with an object and a starting children array
const lines = string.split("\n");
const maxDepth = Math.max(
...lines.map(line => line.replace(/[^\t]/g, "").length)
);
// this one basically closes all square brackets and curly braces
// this is a loop because it depends on the max depth of your source text and i also don't like recursion
for (let index = 0; index < maxDepth - 1; index++) {
json = json.replace(
/(^\t+)(.*,)("children": \[)((\n\1\t+[^\t\n]+)+)/gm,
"$1$2\n$1$3$4\n$1]},"
)
}
// this closes the root object brackets and removes trailing commas and newlines
json = `${json}\n]}`.replace(/,(?=\s*\])/g, "").replace(/\n/g, "");
const object = JSON.parse(json);
const betterLookingJson = JSON.stringify(object, null, "\t");
console.log(object);
console.log(betterLookingJson);

Generate JSON from Tab Tree Text File
The links below attack your problem specifically. All you need to do is update the code so the output is formatted to your requirements.
Python file parsing: Build tree from text file
Creating a tree/deeply nested dict from an indented text file in python
Parse Tree - Ruby example for parsing a tab tree. Created for #ruby on FreeNode.
Tab Delimiter to JSON
Converting Tab Delimited Textfile to JSON - A Python solution you could adapt.
Convert CSV To JSON - Online converter with many options.
Mr. Data Converter - Not an exact solution but you could adapt the code (fork on GitHub).
Other Help
How convert tsv to Json
Convert comma separated list into JSON using Javascript

Related

revise deeply nested JSON array in DOM

I have an HTML page that contains a stringified JSON object. The object has this structure:
{
"x":{
"key1":[],
"key2":{},
"keyN":{},
"myKey":{
"randomID238492":{
"items":[
{ "value":"zzzz" },
{ "value":"aaaa" },
{ ...}
]
}
}
}
}
I want to replace this object with one in which the "items" array has been sorted. Here is what I will and won't know about the object:
"myKey" and "items" will always be the relevant object keys
"myKey" will contain only one random ID, and the "items" key will always be its first child
I won't know the order of "myKey" in the object.
I won't know the true randomID under which "items" nests.
Is there a clear, efficient way to replace this JSON object with one in which "items" has been sorted? Right now, I do it by using this jQuery function after the page has rendered:
$(function() {
var myData = $( "#myJSON_string" )[0]; // <script> node that contains the string
var myDataJSON = JSON.parse(myData.innerText); // JSON string
var myKeyJSON = myDataJSON["x"]["myKey"]; // object
var myArr = myKeyJSON[Object.keys(myKeyJSON)[0]]["items"]; // array to sort
// Now sort and revise. I'm leaving myCompare() out of the example for brevity
myKeyJSON[Object.keys(myKeyJSON)[0]]["items"] = myArr.sort(myCompare);
myDataJSON["x"]["myKey"] = myKeyJSON;
myDataJSON = JSON.stringify(myDataJSON);
myData.innerText = myDataJSON;
});
This approach works, but it seems rather labored. It might be better, for example, if I could revise the JSON object "in place" without parsing it and then re-stringifying it.
Many SO posts, like this one, speak to the general question of how to sort a JSON array. But I can't see that any speak to the specific question posed here.

Is there a way to iterate and display the list of key value pairs of json using Handlebar.js

As Iam new to javascript, I found handleBar.js can be used to template with dynamic data.
I worked on a sample which worked fine and the json structure was simple and straight forward.
(function()
{
var wtsource = $("#some-template").html();
var wtTemplate = Handlebars.compile(wtsource);
var data = { users: [
{url: "index.html", name: "Home" },
{url: "aboutus.html", name: "About Us"},
{url: "contact.html", name: "Contact"}
]};
Handlebars.registerHelper('iter', function(context, options) {
var fn = options.fn, inverse = options.inverse;
var ret = "";
if(context && context.length > 0) {
for(var i=0, j=context.length; i<j; i++) {
ret = ret + fn($.extend({}, context[i], { i: i, iPlus1: i + 1 }));
}
} else {
ret = inverse(this);
}
return ret;
});
var temp=wtTemplate(data);
$("#content").html(temp);
})();
<script id="some-template" type="text/x-handlebars-template">
{{#iter users}}
<li>
{{name}}
</li>
{{/iter}}
</script>
How to iterate a json with the below structure ? Please do suggest the possible way for iterating and creating the template for the below json structure
var newData = { "NEARBY_LIST": {
"100": {
"RestaurantID": 100,
"ParentRestaurantID": 0,
"RestaurantName": "Chennai Tiffin",
"listTime": [{
"startTime": "10:00",
"closeTime": "23:30"
} ]
},
"101": {
"RestaurantID": 101,
"ParentRestaurantID": 0,
"RestaurantName": "Biriyani Factory",
"listTime": [{
"startTime": "11:00",
"closeTime": "22:00"
}]
}
}
};
Accessing the properties of an object has nothing to do with Handlebars. If you dealing with JSON and you wish to access it in general bracket or dot notation, you must first parse the JSON into a JavaScript object using the JSON.parse() function.
After this is done, you may access the properties as follows.
var property = newData['NEARBY_LIST']['100'].RestaurantName; // "Chennai Tiffin"
Here is a fiddle to illustrate.
http://jsfiddle.net/qzm0cygu/2/
I'm not entirely sure what you mean, but if your question is how you can use/read the data in newData, try this:
newData = JSON.parse(newData); //parses the JSON into a JavaScript object
Then access the object like so:
newData.NEARBY_LIST //the object containing the array
newData.NEARBY_LIST[0] //the first item (key "100")
newData.NEARBY_LIST[1] //the second item (key "101")
newData.NEARBY_LIST[0][0] //the first field of the first item (key "RestaurantID", value "100")
newData.NEARBY_LIST[0][2] //the third field of the first item (key "RestaurantName", value "Chennai Tiffin")
newData.NEARBY_LIST[0][3][0] //the first field of the fourth field of the first item (key "startTime", value "11:00")
I hope this was what you were looking for.
EDIT: as Siddharth points out, the above structure does assume you have arrays. If you are not using arrays you can access the properties by using their names as if they're in an associative array (e.g. newData["NEARBY_LIST"]["100"]. The reason I say "properties" and "as if" is because technically JavaScript doesn't support associative arrays. Because they are technically properties you may also access them like newData.NEARBY_LIST (but I don't recommend that in this case as a property name may not start with a number, so you would have to use a mix of the different notations).
On that note, I would recommend using arrays because it makes so many things easier (length checks, for example), and there are practically no downsides.
EDIT2: also, I strongly recommend using the same camelcasing conventions throughout your code. The way you currently have it (with half your properties/variables starting with capitals (e.g. "RestaurantName", "RestaurantID") and the other half being in lowerCamelCase (e.g. "listTime", "startTime")) is just asking for people (you or colleagues) to make mistakes.

What are the best ways to reference branches of a JSON tree structure?

So I've got a JSON file that gets parsed into an object in Javascript. I know what you're thinking: lucky guy. The JSON is essentially a flow diagram in a big tree form. Here's tiny a sample of what I'm trying to achieve:
tree = {
"options": [
{
"options": [
{
"name": "target",
},
],
},
{
"options": [
{
"link": "...?",
},
],
},
]
}
So in this example, I'll be deep in the second branch (where it says "link") and I'll want to be able to jump to the branch that contains "name": "target". This is JSON remember so it'll need to be a string (unless there's a native for linking?! is there?) but I don't know how best to format that.
As I see it, I've got at least a couple of options.
I could search. If name was unique, I could scale the tree looking for elements until I found it. I've never done with with Javascript before but I expect it to be slow.
I could use a navigation path like options:1:options:1 that describes each key for the path. Again, I've never done this but, assuming there are no errors, it would be a lot faster. How would you implement it?
Are there any other options available to me? What seems best? Is there a way to unpack this when JSON decoding, or is that a recipe for an infinite loop?
What about link: 'tree.options[0].options[0]' then eval(path.to.link)?
Following samples were tested with Chrome only. Same tree for all :
var tree = { level1: [{ key: 'value' }] };
No eval
function resolve(root, link) {
return (new Function('root', 'return root.' + link + ';'))(root);
}
var value = resolve(tree, path.to.link);
Fallback to window
function resolve(root, link) {
return (new Function(
'root', 'return root.' + (link || root) + ';'
))(link ? root : window);
}
resolve(tree, 'level1[0].key'); // "value"
resolve('tree.level1[0].key'); // "value"
Catching errors
The try/catch block prevents broken links from throwing errors.
function resolve(root, path) {
try {
return (new Function('root', 'return root.' + path + ';'))(root);
} catch (e) {}
}
resolve(tree, 'level1[0].key'); // "value"
resolve(tree, 'level1[1].key'); // undefined
Using custom path format
The good part here is that we can pass either an object or an array as root. Also note that we can replace the slash in path.split('/') with any char of our choice.
function resolve(root, path) {
path = '["' + path.split('/').join('"]["') + '"]';
return (new Function('root', 'return root' + path + ';'))(root);
}
resolve(tree.level1, '0/key'); // "value"
resolve(tree, 'level1/0/key'); // "value"
resolve(tree, 'level1/0'); // Object {key: "value"}

Creating a nested JSON object dynamically?

I am almost there with this but cannot seem to get this functionality going as planned.
I have json 'jsonData' it contains formula of different terms
"jsonData":{
"a" : "b + c",
"b" : "d + e",
"d" : "h + i",
"c" : "f + g"
}
What I am trying to do is to have a function pass one arguments 'mainItem'(ie. one of the key in 'jsonData' for example a in 'jsonData'). Within this function it will get the formula from the json data(for example a is the 'mainitem' and b + c is the formula) and check the dependency of child component of the formula i.e it will check whether b and c have any dependency down the line in json data. If it has any dependency it will be added as a child component to the parent for example if b have a formula in json data. b will be added as the 'mainitem' in the child component of the parent 'mainitem' a. At the end of the code, this is what I would like to get.
{
mainitem : "a",
formula : "b+c",
childComponent: {
mainitem: "b",
formula : "d+e",
childcomponent: {
mainitem: "d",
formula : "h+i"
}
},
{
mainitem: "c",
formula : "f+g"
},
}
The issue is that I am able to create the parent object. But I have no idea how to create the child component for the parent and If the child component have sub child it will also embedded as the child of the child component and so on. It is like a parent child hierarchy series
function getJson(mainItem) {
var json = {};
json['mainitem'] = mainItem;
$.each(jsonData, function(key, value){
if(mainitem == key){
json['formula'] = value;
}
})
}
Any insight into this would highly be appreciated. Thank you.
You need/could to write a recursive function that splits up the "formula" into each composing component/item and then check each component/item for their dependencies.
Here is a solution for you: http://jsfiddle.net/mqchen/4x7cD/
function getJson(item, data) {
if(!data["jsonData"].hasOwnProperty(item)) return null;
var out = {
mainItem: item,
formula: data["jsonData"][item]
};
// Break up formula
var components = out.formula.split(" ");
for(var i = 0; i < components.length; i++) {
var child = getJson(components[i], data); // Recursive call to get childComponents
if(child !== null) {
out["childComponent"] = out["childComponent"] == undefined ? [] : out["childComponent"];
out["childComponent"].push(child);
}
}
return out;
}
// Call it
getJson("a", data)
Note: It does not consider circular dependencies, i.e. if you have a: "b + c", b: "d + a".
This is a dependency problem. I can already tell you ahead of time that you will need to figure out a way to handle circular dependencies (you don't want to get thrown into an infinite loop/recursion when trying to generate the output). Let's go through the basic algorithm. What are some things we need?
We need a way to parse the formulas so that they're meaningful. For this example, I'm gonna assume that we'll get an input that's of the form a + b + c, where I only expect the item and + to delimit each item.
We need a way to recurse down an item's dependencies to create nested childcomponents.
// assuming jsonData is available in this scope
function getStructure (elem) {
elem = $.trim(elem);
// handling an element that doesn't exist in jsonData
if (!jsonData[elem]) {
return {
'mainitem': elem,
'formula': 'none' // or whatever you want to put
};
}
var result = {},
formula = jsonData[elem],
children = formula.split('+'),
i;
result['mainitem'] = elem;
result['formula'] = formula;
// or however you want to store the child components
result['childComponent'] = [];
for (i = 0; i < children.length; i += 1) {
result['childComponent'].push(getStructure(children[i]));
}
return result;
}
Yeah this is some sort of building Syntax Tree as in classic parser/compiler problem.
Any ways I have written this simple recursive function that does what you want. Though if your goal is to build some sort of parser then you must think of following parser / compiler building principles since that will keep things manageable and graspable once functions start growing.
function getJson(mainitem,outjson)
{
formula = jsonData[mainitem];
outjson.mainitem = mainitem;
if (formula != null)
{
outjson.formula = formula;
var firstItem = formula.toString().charAt(0);
var secondItem = formula.charAt(formula.length - 1);
outjson.firstchild = {};
outjson.secondchild = {};
getJson(firstItem, outjson.firstchild);
getJson(secondItem, outjson.secondchild);
}
}
What all you have to do is to create an empty object and pass it to getJson() along with the operand in problem i.e. mainitem:
var outjson = {};
getJson("a", outjson);
I have used JSON library to convert outjson object to JSON text.
Also I have logged this outjson so that you can examine it in the embedded firebug lites' Console window.
Find it at JSFiddle.
There is approved answer already but here are my 5 cents.
as #Mahesha999 said you need to build "Syntax Tree as in classic parser/compiler".
For some theory + examples look at those videos
They are focused on antlr but also contains a lot theory about parsers. Also antlr have javascript plugin that can be used.
I think it's better than any expression evaluation.

how to encode this data to parent / children structure in JSON

I am working with d3.js to visualise families of animals (organisms) (up to 4000 at a time) as a tree graph, though the data source could just as well be a directory listing, or list of namespaced objects. my data looks like:
json = {
organisms:[
{name: 'Hemiptera.Miridae.Kanakamiris'},
{name: 'Hemiptera.Miridae.Neophloeobia.incisa'},
{name: 'Lepidoptera.Nymphalidae.Ephinephile.rawnsleyi'},
... etc ...
]
}
my question is: I am trying to find the best way to convert the above data to the hierarchical parent / children data structure as is used by a number of the d3 visualisations such as treemap (for data example see flare.json in the d3/examples/data/ directory).
Here is an example of the desired data structure:
{"name": "ROOT",
"children": [
{"name": "Hemiptera",
"children": [
{"name": "Miridae",
"children": [
{"name": "Kanakamiris", "children":[]},
{"name": "Neophloeobia",
"children": [
{"name": "incisa", "children":[] }
]}
]}
]},
{"name": "Lepidoptera",
"children": [
{"name": "Nymphalidae",
"children": [
{"name": "Ephinephile",
"children": [
{"name": "rawnsleyi", "children":[] }
]}
]}
]}
]}
}
EDIT: enclosed all the original desired data structure inside a ROOT node, so as to conform with the structure of the d3 examples, which have only one master parent node.
I am looking to understand a general design pattern, and as a bonus I would love to see some solutions in either javascript, php, (or even python). javascript is my preference.
In regards to php: the data I am actually using comes from a call to a database by a php script that encodes the results as json.
database results in the php script is an ordered array (see below) if that is any use for php based answers.
Array
(
[0] => Array
(
['Rank_Order'] => 'Hemiptera'
['Rank_Family'] => 'Miridae'
['Rank_Genus'] => 'Kanakamiris'
['Rank_Species'] => ''
) ........
where:
'Rank_Order' isParentOf 'Rank_Family' isParentOf 'Rank_Genus' isParentOf 'Rank_Species'
I asked a similar question focussed on a php solution here, but the only answer is not working on my server, and I dont quite understand what is going on, so I want to ask this question from a design pattern perspective, and to include reference to my actual use which is in javascript and d3.js.
The following is specific to the structure you've provided, it could be made more generic fairly easily. I'm sure the addChild function can be simplified. Hopefully the comments are helpful.
function toHeirarchy(obj) {
// Get the organisms array
var orgName, orgNames = obj.organisms;
// Make root object
var root = {name:'ROOT', children:[]};
// For each organism, get the name parts
for (var i=0, iLen=orgNames.length; i<iLen; i++) {
orgName = orgNames[i].name.split('.');
// Start from root.children
children = root.children;
// For each part of name, get child if already have it
// or add new object and child if not
for (var j=0, jLen=orgName.length; j<jLen; j++) {
children = addChild(children, orgName[j]);
}
}
return root;
// Helper function, iterates over children looking for
// name. If found, returns its child array, otherwise adds a new
// child object and child array and returns it.
function addChild(children, name) {
// Look for name in children
for (var i=0, iLen=children.length; i<iLen; i++) {
// If find name, return its child array
if (children[i].name == name) {
return children[i].children;
}
}
// If didn't find name, add a new object and
// return its child array
children.push({'name': name, 'children':[]});
return children[children.length - 1].children;
}
}
Given your starting input I believe something like the following code will produce your desired output. I don't imagine this is the prettiest way to do it, but it's what came to mind at the time.
It seemed easiest to pre-process the data to first split up the initial array of strings into an array of arrays like this:
[
["Hemiptera","Miridae","Kanakamiris" ],
["Hemiptera","Miridae","Neophloeobia","incisa" ],
//etc
]
...and then process that to get a working object in a form something like this:
working = {
Hemiptera : {
Miridae : {
Kanakamiris : {},
Neophloeobia : {
incisa : {}
}
}
},
Lepidoptera : {
Nymphalidae : {
Ephinephile : {
rawnsleyi : {}
}
}
}
}
...because working with objects rather than arrays makes it easier to test whether child items already exist. Having created the above structure I then process it one last time to get your final desired output. So:
// start by remapping the data to an array of arrays
var organisms = data.organisms.map(function(v) {
return v.name.split(".");
});
// this function recursively processes the above array of arrays
// to create an object whose properties are also objects
function addToHeirarchy(val, level, heirarchy) {
if (val[level]) {
if (!heirarchy.hasOwnProperty(val[level]))
heirarchy[val[level]] = {};
addToHeirarchy(val, level + 1, heirarchy[val[level]]);
}
}
var working = {};
for (var i = 0; i < organisms.length; i++)
addToHeirarchy(organisms[i], 0, working);
// this function recursively processes the object created above
// to create the desired final structure
function remapHeirarchy(item) {
var children = [];
for (var k in item) {
children.push({
"name" : k,
"children" : remapHeirarchy(item[k])
});
}
return children;
}
var heirarchy = {
"name" : "ROOT",
"children" : remapHeirarchy(working)
};
Demo: http://jsfiddle.net/a669F/1/
An alternative answer to my own question....In the past day I have learn't a great deal more about d3.js and in relation to this question d3.nest() with .key() and .entries() is my friend (all d3 functions).
This answer involves changing the initial data, so it may not qualify as a good answer to the specific question i asked. However if someone has a similar question and can change things on the server then this is a pretty simple solution:
return the data from the database in this format:
json = {'Organisms': [
{ 'Rank_Order': 'Hemiptera',
'Rank_Family': 'Miridae',
'Rank_Genus': 'Kanakamiris',
'Rank_Species': '' },
{}, ...
]}
Then using d3.nest()
organismNest = d3.nest()
.key(function(d){return d.Rank_Order;})
.key(function(d){return d.Rank_Family;})
.key(function(d){return d.Rank_Genus;})
.key(function(d){return d.Rank_Species;})
.entries(json.Organism);
this returns:
{
key: "Hemiptera"
values: [
{
key: "Cicadidae"
values: [
{
key: "Pauropsalta "
values: [
{
key: "siccanus"
values: [
Rank_Family: "Cicadidae"
Rank_Genus: "Pauropsalta "
Rank_Order: "Hemiptera"
Rank_Species: "siccanus"
AnotherOriginalDataKey: "original data value"
etc etc, nested and lovely
This returns something very much similar to they array that I described as my desired format above in the question, with a few differences. In particular, There is no all enclosing ROOT element and also whereas they keys I originally wanted were "name" and "children" .nest() returns keys as "key" and "values" respectively.
These alternatives keys are easy enough to use in d3.js by just defining appropriate data accessor functions (basic d3 concept) ... but that is getting beyond the original scope of the question ... hope that helps someone too

Categories