5

How to convert a string to JSON with javascript or jQuery? I've been thinking all day, but I do not get a good idea.

This task is to dynamically create the treeview in the client side (ASP.Net). My idea is to convert the string to an object and convert to JSON type. (String -> object -> JSON) I tried, but the day is gone. It is difficult to construct 2 more depth like A->a3->a31.

String is

var sString = "A//a1,A//a2,A//a3//a31,A//a3//a32,B,C//c1,C//c2";

and JSON format is

{
  "title": "A",
  "key": "1",
  "folder": true,
  "children": [{
      "title": "a1",
      "key": "2"
    }, {
      "title": "a2",
      "key": "3"
    }, {
      "title": "a3",
      "key": "4",
      "folder": true,
      "children": [{
          "title": "a31",
          "key": "5"
        }...
      }]
  }

(This is fancytreeview plugin)

‘//‘ is depth and ‘,’ is split.

Please help me..

enter image description here

Edit) I want to turn ‘sString’ to JSON format.. but It’s ok just JSON type string.

Please understand that my sentence is strange because my native language is not English.

Edit2) oh.. I want to convert the string to an object and then convert it back to JSON format. I do not have the confidence to convert that string into JSON format right away. Because there are more than 8000 variants. If It’s can, let me know how.

cherryJang
  • 75
  • 5
  • 5
    Not a direct answer to your question, but might give you some ideas. "Recursion" is the keyword here. – Teemu Dec 05 '17 at 11:23
  • 1
    Are you trying to turn the JSON to that string format, or the other way around? It's confusing as both are strings. – Rory McCrossan Dec 05 '17 at 11:24
  • @RoryMcCrossan _"convert a string to JSON"_ makes me think the OP wants to convert the "A//a1" string into a JSON string. – evolutionxbox Dec 05 '17 at 11:26
  • That was my thought too, but just wanted to double check. – Rory McCrossan Dec 05 '17 at 11:26
  • That's not [JSON](http://json.org). _"JSON is a textual, language-indepedent data-exchange format, much like XML, CSV or YAML."_ - [What is the difference between JSON and Object Literal Notation?](https://stackoverflow.com/questions/2904131/what-is-the-difference-between-json-and-object-literal-notation) – Andreas Dec 05 '17 at 11:28
  • 1
    @Andreas I wouldn't consider this distinction helpful as the format the OP would like is valid JSON _once stringified_. – evolutionxbox Dec 05 '17 at 11:30
  • @RoryMcCrossan I want to turn ‘sString’ to JSON format.. but It’s ok just JSON type string. – cherryJang Dec 05 '17 at 11:32
  • @evolutionxbox It is, otherwise the question would not have been asked in the first place... `sString` converted to JSON -> `sString = JSON.stringify(sString);` - Doesn't make much sense, or is it...? O.o The task is to convert the custom format `sString` has into an actual object. – Andreas Dec 05 '17 at 11:35
  • @Andreas oh.. I want to convert the string to an object and then convert it back to JSON format. I do not have the confidence to convert that string into JSON format right away. Because there are more than 8000 variants. If you can, let me know how. – cherryJang Dec 05 '17 at 11:51

3 Answers3

4

I believe this can be done without recursion:

var string = "A//a1,A//a2,A//a3//a31,A//a3//a32,B,C//c1,C//c2";

// Take all the roots
var roots = string.split(',');

// We will attach it to every node and keep it incrementing
var key = 1;

// The final result will be in this object
var result = [];

// Loop through to found roots
roots.forEach(function(root) {
  // Take all the children
  var items = root.split('//');
  var parent = result;

  // Loop through the available children
  items.forEach(function(item, i) {
    // Find if the current item exists in the tree
    var child = getChild(parent, item);

    if (!child) {
     child = {
        title: item,
        key: key++
      }
      
      // This will ensure that the current node is a folder only
      // if there are more children
      if (i < items.length - 1) {
       child.folder = true;
        child.children = [];
      }
      
      // Attach this node to parent
      parent.push(child);
    }
    
    parent = child.children;
  });
});

console.log(result);

// Utility function to find a node in a collection of nodes by title
function getChild(parent, title) {
  for (var i = 0; i < parent.length; i++) {
    if (parent[i].title === title) {
      return parent[i];
    }
  }
}

This is the draft code which came in my mind at first. I believe it can be improved further in terms of complexity.

31piy
  • 23,323
  • 6
  • 47
  • 67
  • This is gold. Nice. – nikamanish Dec 05 '17 at 11:58
  • Thank you so much! I'll try it tomorrow! My area is now at night and I can not do it right now. Thanks! – cherryJang Dec 05 '17 at 12:03
  • @cherryJang -- As per your comment on [this post](https://stackoverflow.com/q/47668115/2019247), I've changed the code to avoid arrow functions. – 31piy Dec 06 '17 at 07:13
  • @31piy It's working! Thanks! but I don't want this..! each node separate.. I want 'A//a1,A//a2' to 'A//a1,a2' and then convert to JSON type.. – cherryJang Dec 06 '17 at 07:40
  • @cherryJang -- The answer matches the JSON representation you mentioned in your post. If you have a different requirement, post that as a new question. – 31piy Dec 06 '17 at 07:48
  • @31piy I mean.. a1, a2, a3 as children in the title 'A' and I need only one title 'A'. But this is a duplicate 'A' title... – cherryJang Dec 06 '17 at 08:08
  • @cherryJang -- I'm afraid I cannot clearly understand your requirement. There is only one 'A' in the result. – 31piy Dec 06 '17 at 08:24
  • @31piy oh really? um.. I edited 'getChild' function because I can not use 'find' method.. maybe I made a mistake.. can you change 'find' method ? – cherryJang Dec 06 '17 at 08:44
  • @cherryJang -- Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/160582/discussion-between-31piy-and-cherryjang). – 31piy Dec 06 '17 at 09:03
0
var key = 1; // keys start at 1

let addPaths = (root, paths) => {

    if (!paths || paths.length == 0)
        return;

    let path = paths.shift();

    //add nodes for the current path
    addNodes(root, path.split('//'));

    // keep going until all paths have been processed
    addPaths(root, paths);
};

let addNodes = (root, nodeList) => {

    if (!nodeList || nodeList.length == 0)
        return;

    let title = nodeList.shift();

    // find node under root with matching title
    let isRootNode = Array.isArray(root);

    node = (isRootNode ? root : root.children || []).find((node) => {
        return node.title == title;
    });


    if (!node){
        node = {
            title: title,
            key: key++
        }

        // are we at root of object?
        if (isRootNode)
            root.push(node);
        else
        {
            if (!root.children)
                root.children = [];

            root.children.push(node);
            root.folder = true;
        }
    }

    addNodes(node, nodeList);
};

let parse = (string) => {
    let object = [];

    let nodes = string.split(',');

    addPaths(object, nodes);

    return object
};

console.log(JSON.stringify(parse("A//a1,A//a2,A//a3//a31,A//a3//a32,B,C//c1,C//c2"), null, 2));

Which results in:

[
  {
    "title": "A",
    "key": 1,
    "children": [
      {
        "title": "a1",
        "key": 2
      },
      {
        "title": "a2",
        "key": 3
      },
      {
        "title": "a3",
        "key": 4,
        "children": [
          {
            "title": "a31",
            "key": 5
          },
          {
            "title": "a32",
            "key": 6
          }
        ],
        "folder": true
      }
    ],
    "folder": true
  },
  {
    "title": "B",
    "key": 7
  },
  {
    "title": "C",
    "key": 8,
    "children": [
      {
        "title": "c1",
        "key": 9
      },
      {
        "title": "c2",
        "key": 10
      }
    ],
    "folder": true
  }
]
phuzi
  • 12,078
  • 3
  • 26
  • 50
0

Try below code. I have used associative array to store already processed folder for faster lookup.

I hope it helps you.

var sString = "A//a1,A//a2,A//a3//a31,A//a3//a32,B,C//c1,C//c2";

var sArr = sString.split(","); // We will split it by comma so that we can iterate through its items.
var output = []; // Final result will be stored here.
var hash = {}; // It used to keep track of itemObjectect's position for faster lookup.
var counter = 1; // Its value will be used to assign to key;
for(var i = 0; i < sArr.length; i++){
    var items = sArr[i].split("//"); 
    var itemObject = {}; // Object to store value of each item.
    var parentItemObject = {}; // It will refer to current parentObject during iteration.
    for(var j = 0; j < items.length; j++){
   // Check if item is already processed and stored in hash map.
   if(hash.hasOwnProperty(items[j])){
     // Check if parent Object value is empty then we will fetch it from hash directly.
     if(isEmpty(parentItemObject)){
    parentItemObject = output[hash[items[j]]];
     }
     else{
    // It is parent element but is child of another element. Then we will fetch it from it's children array.
    if(typeof parentItemObject.children !== "undefined"){
      parentItemObject = parentItemObject.children[hash[items[j]]];
    }
     }
     continue;
   }  
   
    itemObject.title = items[j];
    itemObject.key = counter++;
     
     // Check if it is a folder item.
     if(j != items.length -1){
    itemObject.folder = true;
    itemObject.children = [];
    if(isEmpty(parentItemObject)){
     parentItemObject = itemObject;
     hash[itemObject.title] = output.length;
     output.push(itemObject);
    }
    else{
     if(typeof parentItemObject.children !== "undefined"){
       hash[itemObject.title] = parentItemObject.children.length;
       parentItemObject.children.push(itemObject);
     }
     parentItemObject = itemObject;
    }
     }
     else{
    
    if(isEmpty(parentItemObject)){
     parentItemObject = itemObject;
     hash[itemObject.title] = output.length;
     output.push(itemObject);
    }
    if(typeof parentItemObject.children !== "undefined"){
      hash[itemObject.title] = parentItemObject.children.length;
      parentItemObject.children.push(itemObject);
    }
     }
   
   itemObject = {};
    }
    
    //console.log(items);
  }

function isEmpty(itemObject) {
  return Object.keys(itemObject).length === 0;
}

//console.log(hash);
console.log(JSON.stringify(output,null,2));
Lalit
  • 1,354
  • 1
  • 8
  • 13
  • Thanks a lot! I tried now.. but i got error..! my stirng is "T1a//Ch1//OP01,T1b//Ch1//OP01". Please try this.. – cherryJang Dec 06 '17 at 02:18