rob.m
rob.m

Reputation: 10571

Converting nested list into a nested object?

The following almost works but if i remove the <a> the json breaks with an empty node name:" " and also i believe i am creating an array while i need the json in the format below. Here it is a jsFiddle too (see below the result for the output i am looking for).

What I am trying to do is to generate an object I can read from a nested list in order to generate a mind map (D3) like this

HTML

<div id="tree">
<ul class="sortable">
  <li><a href="http://google.com">flare</a>
    <ul class="sortable">
      <li>analytics
        <ul class="sortable">
          <li><a href="http://google.com">cluster</a>
            <ul class="sortable">
              <li><a href="http://google.com">AgglomerativeCluster</a></li>
              <li><a href="http://google.com">CommunityStructure</a></li>
              <li><a href="http://google.com">HierarchicalCluster</a></li>
              <li><a href="http://google.com">MergeEdge</a></li>
            </ul>
          </li>
          <li><a href="http://google.com">graph</a>
            <ul class="sortable">
              <li><a href="http://google.com">BetweennessCentrality</a></li>
              <li><a href="http://google.com">LinkDistance</a></li>
              <li><a href="http://google.com">MaxFlowMinCut</a></li>
              <li><a href="http://google.com">ShortestPaths</a></li>
              <li><a href="http://google.com">SpanningTree</a></li>
            </ul>
          </li>
          <li><a href="http://google.com">optimization</a>
            <ul class="sortable">
              <li><a href="http://google.com">AspectRatioBanker</a></li>
            </ul>
          </li>
        </ul>
      </li>
    </ul>
  </li>
  <li><a href="http://google.com">Pippo</a></li>
  <li><a href="http://google.com">buonanotte</a></li>
  <li><a href="http://google.com">Avantieri</a></li>
</ul>
</div>
<div id="d" style="margin-top: 40px; padding-top: 20px;">Output:<br><br><pre></pre></div>


<div id="d" style="margin-top: 40px; padding-top: 20px;">Output:<br><br><pre></pre></div>
<div id="json_results" style="margin-top: 40px; padding-top: 20px;">
Needs to become:
  <pre style="padding-top: 20px;">
var root = JSON.parse( ' {
 "name": "flare",
 "children": [
  {
   "name": "analytics",
   "children": [
    {
     "name": "cluster",
     "children": [
      {"name": "AgglomerativeCluster","url":"http://google.com"},
      {"name": "CommunityStructure"},
      {"name": "HierarchicalCluster"},
      {"name": "MergeEdge"}
     ]
    },
    {
     "name": "graph",
     "children": [
      {"name": "BetweennessCentrality" },
      {"name": "LinkDistance" },
      {"name": "MaxFlowMinCut" },
      {"name": "ShortestPaths" },
      {"name": "SpanningTree" }
     ]
    },
    {
     "name": "optimization",
     "children": [
      {"name": "AspectRatioBanker" }
     ]
    }
   ]
  },
  {"name": "Pippo"},
  {"name": "buonanotte" },
  {"name": "Avantieri"}
 ]
} ') ;
  </pre>
</div>

JS

$(document).ready(function () {
    var out = [];
    function processOneLi(node) {       
        var aNode = node.children("a:first");
        var retVal = {
            "name": aNode.text(),
            "url": aNode.attr("href")
        };
        node.find("> .sortable > li").each(function() {
            if (!retVal.hasOwnProperty("children")) {
                retVal.children = [];
            }
            retVal.children.push(processOneLi($(this)));
        });
        return retVal;
    }
    $("#tree > ul > li").each(function() {
        out.push(processOneLi($(this)));
    });
    $('#d pre').text(JSON.stringify(out, null, 2));

});

The json should become:

var root = JSON.parse( ' {
 "name": "flare",
 "children": [
  {
   "name": "analytics",
   "children": [
    {
     "name": "cluster",
     "children": [
      {"name": "AgglomerativeCluster","url":"http://google.com"},
      {"name": "CommunityStructure"},
      {"name": "HierarchicalCluster"},
      {"name": "MergeEdge"}
     ]
    },
    {
     "name": "graph",
     "children": [
      {"name": "BetweennessCentrality" },
      {"name": "LinkDistance" },
      {"name": "MaxFlowMinCut" },
      {"name": "ShortestPaths" },
      {"name": "SpanningTree" }
     ]
    },
    {
     "name": "optimization",
     "children": [
      {"name": "AspectRatioBanker" }
     ]
    }
   ]
  },
  {"name": "Pippo"},
  {"name": "buonanotte" },
  {"name": "Avantieri"}
 ]
} ') ;

but this is what i get instead:

[
  {
    "name": "flare",
    "url": "http://google.com",
    "children": [
      {
        "name": "",
        "children": [
          {
            "name": "cluster",
            "url": "http://google.com",
            "children": [
              {
                "name": "AgglomerativeCluster",
                "url": "http://google.com"
              },
              {
                "name": "CommunityStructure",
                "url": "http://google.com"
              },
              {
                "name": "HierarchicalCluster",
                "url": "http://google.com"
              },
              {
                "name": "MergeEdge",
                "url": "http://google.com"
              }
            ]
          },
          {
            "name": "graph",
            "url": "http://google.com",
            "children": [
              {
                "name": "BetweennessCentrality",
                "url": "http://google.com"
              },
              {
                "name": "LinkDistance",
                "url": "http://google.com"
              },
              {
                "name": "MaxFlowMinCut",
                "url": "http://google.com"
              },
              {
                "name": "ShortestPaths",
                "url": "http://google.com"
              },
              {
                "name": "SpanningTree",
                "url": "http://google.com"
              }
            ]
          },
          {
            "name": "optimization",
            "url": "http://google.com",
            "children": [
              {
                "name": "AspectRatioBanker",
                "url": "http://google.com"
              }
            ]
          }
        ]
      }
    ]
  },
  {
    "name": "Pippo",
    "url": "http://google.com"
  },
  {
    "name": "buonanotte",
    "url": "http://google.com"
  },
  {
    "name": "Avantieri",
    "url": "http://google.com"
  }
]

Here it is the jsFiddle (see below the result for the output i am looking for)

Upvotes: 0

Views: 208

Answers (3)

Tomalak
Tomalak

Reputation: 338228

A small recursive function will do:

function listToObject(list) {
  return $(list).first().children("li").map(function () {
    var $this = $(this), $name = $this.children("a").first();
    return {
                            // see http://stackoverflow.com/a/8851526
      name: $name.text() || $this.clone().children().remove().end().text().trim(),
      url: $name.attr("href"),
      children: listToObject($this.children("ul"))
    };
  }).toArray();
}

var output = listToObject($("#tree > ul"));
console.log(output);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

<div id="tree">
  <ul class="sortable">
    <li><a href="http://google.com">flare</a>
      <ul class="sortable">
        <li>analytics
          <ul class="sortable">
            <li><a href="http://google.com">cluster</a>
              <ul class="sortable">
                <li><a href="http://google.com">AgglomerativeCluster</a></li>
                <li><a href="http://google.com">CommunityStructure</a></li>
                <li><a href="http://google.com">HierarchicalCluster</a></li>
                <li><a href="http://google.com">MergeEdge</a></li>
              </ul>
            </li>
            <li><a href="http://google.com">graph</a>
              <ul class="sortable">
                <li><a href="http://google.com">BetweennessCentrality</a></li>
                <li><a href="http://google.com">LinkDistance</a></li>
                <li><a href="http://google.com">MaxFlowMinCut</a></li>
                <li><a href="http://google.com">ShortestPaths</a></li>
                <li><a href="http://google.com">SpanningTree</a></li>
              </ul>
            </li>
            <li><a href="http://google.com">optimization</a>
              <ul class="sortable">
                <li><a href="http://google.com">AspectRatioBanker</a></li>
              </ul>
            </li>
          </ul>
        </li>
      </ul>
    </li>
    <li><a href="http://google.com">Pippo</a></li>
    <li><a href="http://google.com">buonanotte</a></li>
    <li><a href="http://google.com">Avantieri</a></li>
  </ul>
</div>

The output is a bit stricter than what you have asked for. Each object has a url and children, even if empty. This will actually make handling easier in the end.

Upvotes: 1

Luis Estevez
Luis Estevez

Reputation: 1407

I found your problem, there was one li element that didn't have element which you were looking for.

This will get you the results you're looking

$(document).ready(function () {
    var out = [];
    function processOneLi(node) {
        var aNode = node.children("a:first");
        var retVal = {};
        if (aNode.text()) {
           retVal = {
              "name": aNode.text(),
              "url": aNode.attr("href")
           };
        } else {
          retVal = { "name": node.clone().children().remove().end().text().replace(/\r?\n|\r/g, " ") };
        }
        node.find("> .sortable > li").each(function() {
            if (!retVal.hasOwnProperty("children")) {
                retVal.children = [];
            }
            retVal.children.push(processOneLi($(this)));
        });
        return retVal;
    }
    $("#tree > ul > li").each(function() {
        out.push(processOneLi($(this)));
    });
    $('#d pre').text(JSON.stringify(out, null, 2));

});

Upvotes: 0

Matansh
Matansh

Reputation: 782

Your problem is that Pippo, buonanotte and Avantieri are siblings of flare, instead of being nested under flare.

I think I got to the result you expected in this fiddle: http://jsfiddle.net/k7vSg/122/

HTML:

<div id="tree">
<ul class="sortable">
  <li><a href="http://google.com">flare</a>
    <ul class="sortable">
      <li>analytics
        <ul class="sortable">
          <li><a href="http://google.com">cluster</a>
            <ul class="sortable">
              <li><a href="http://google.com">AgglomerativeCluster</a></li>
              <li><a href="http://google.com">CommunityStructure</a></li>
              <li><a href="http://google.com">HierarchicalCluster</a></li>
              <li><a href="http://google.com">MergeEdge</a></li>
            </ul>
          </li>
          <li><a href="http://google.com">graph</a>
            <ul class="sortable">
              <li><a href="http://google.com">BetweennessCentrality</a></li>
              <li><a href="http://google.com">LinkDistance</a></li>
              <li><a href="http://google.com">MaxFlowMinCut</a></li>
              <li><a href="http://google.com">ShortestPaths</a></li>
              <li><a href="http://google.com">SpanningTree</a></li>
            </ul>
          </li>
          <li><a href="http://google.com">optimization</a>
            <ul class="sortable">
              <li><a href="http://google.com">AspectRatioBanker</a></li>
            </ul>
          </li>
        </ul>
      </li>
      <li><a href="http://google.com">Pippo</a></li>
      <li><a href="http://google.com">buonanotte</a></li>
      <li><a href="http://google.com">Avantieri</a></li>
    </ul>
  </li>

</ul>
</div>



<div id="d" style="margin-top: 40px; padding-top: 20px;">Output:<br><br><pre></pre></div>
<div id="json_results" style="margin-top: 40px; padding-top: 20px;">
Needs to become:
  <pre style="padding-top: 20px;">
var root = JSON.parse( ' {
 "name": "flare",
 "children": [
  {
   "name": "analytics",
   "children": [
    {
     "name": "cluster",
     "children": [
      {"name": "AgglomerativeCluster","url":"http://google.com"},
      {"name": "CommunityStructure"},
      {"name": "HierarchicalCluster"},
      {"name": "MergeEdge"}
     ]
    },
    {
     "name": "graph",
     "children": [
      {"name": "BetweennessCentrality" },
      {"name": "LinkDistance" },
      {"name": "MaxFlowMinCut" },
      {"name": "ShortestPaths" },
      {"name": "SpanningTree" }
     ]
    },
    {
     "name": "optimization",
     "children": [
      {"name": "AspectRatioBanker" }
     ]
    }
   ]
  },
  {"name": "Pippo"},
  {"name": "buonanotte" },
  {"name": "Avantieri"}
 ]
} ') ;
  </pre>
</div>

JS:

$(document).ready(function () {
    var out = [];
    function processOneLi(node) {       
        var aNode = node.children("a:first");
        var retVal = {
            "name": aNode.text(),
            "url": aNode.attr("href")
        };
        node.find("> .sortable > li").each(function() {
            if (!retVal.hasOwnProperty("children")) {
                retVal.children = [];
            }
            retVal.children.push(processOneLi($(this)));
        });
        return retVal;
    }
    $("#tree > ul > li").each(function() {
        out.push(processOneLi($(this)));
    });

    $('#d pre').text(JSON.stringify(out[0], null, 2));

});

Upvotes: 0

Related Questions