简体   繁体   中英

How to convert not nested JSON into nested HTML list in Javascript (or Coffeescript)?

I have some JSON data (simple array of objects) .

var input= [
    {
        "cat": "some",
        "id": "0"
    },
    {
        "cat": "some",
        "id": "1"
    },
    {
        "cat": "some/category",
        "id": "2"
    },
    {
        "cat": "some/category/sub",
        "id": "3"
    },
    {
        "cat": "some/other",
        "id": "4"
    },
    {
        "cat": "some/thing/different",
        "id": "5"
    },
    {
        "cat": "some/thing/different",
        "id": "6"
    },
    {
        "cat": "yet/another",
        "id": "7"
    }
]

I wanted to generate nested html list out of it based on categories:

  • some
    • 0
    • 1
    • category
      • 2
      • sub
        • 3
    • other
      • 4
    • thing
      • different
        • 5
        • 6
  • yet
    • another
      • 7

My first step was to create empty nested object using the function:

createNestedObject = function(base, names) {
  var  = 0;
  var results = [];
  while (i < names.length) {
    base = base[names[i]] = base[names[i]] || {};
    results.push(i++);
  }
  return results;
}

Next i populated it with data using by spliting "cat" strings and pushing "ids" in loop (ex. some.category.sub.ids.push(7)) The final result was:

var output = 
{
    "some": {
        "ids": [
            "0",
            "1"
        ],
        "category": {
            "ids": [
                "2"
            ],
            "sub": {
                "ids": [
                    "3"
                ]
            }
        },
        "other": {
            "ids": [
                "4"
            ]
        },
        "thing": {
            "different": {
                "ids": [
                    "5",
                    "6"
                ]
            }
        },
        "yet": {
            "another": {
                "ids": [
                    "7"
                ]
            }
        }
    }
}

However, the structure somewhat problematic because i don't know the key names and the nesting depth in advance. How to generate nested html list out of "output" or "input" data presented here?

How about this?

Example

JS

function ToHTML(input){
    var html = '<ul>'; 

    for(var key in input){
        if(input[key] instanceof Array){
            for(var i = 0; i < input[key].length; i++){
                html += '<li>' + input[key][i] + '</li>';
            }
        }else{
            html += '<li>' + key + ToHTML(input[key]) + '</li>';
        }           
    }
    html += '</ul>';
    return html; 
}

function ToNestedObject(input){
    var i, y, len = input.length, parts, partsLen, obj = {}, prev;
    for(i = 0; i < len; i++){
        parts = input[i].cat.split('/'); 
        partsLen = parts.length;
        prev = obj; 
        for(y = 0; y < partsLen; y++){
            prev[parts[y]] = prev[parts[y]] || {};    
            prev = prev[parts[y]];
        }
        if(!prev.ids){
            prev.ids = []; 
        }
        prev.ids.push(input[i].id); 
    }
    return obj; 
}

var input= [
    {
        "cat": "some",
        "id": "0"
    },
    {
        "cat": "some",
        "id": "1"
    },
    {
        "cat": "some/category",
        "id": "2"
    },
    {
        "cat": "some/category/sub",
        "id": "3"
    },
    {
        "cat": "some/other",
        "id": "4"
    },
    {
        "cat": "some/thing/different",
        "id": "5"
    },
    {
        "cat": "some/thing/different",
        "id": "6"
    },
    {
        "cat": "yet/another",
        "id": "7"
    }
];

document.getElementById('test').innerHTML = ToHTML(ToNestedObject(input)); 

HTML

<div id='test'></div>

The array is converted into object tree

function buildTreeObject ( input ) {

        var obj = {}, n ;

        input.forEach( function( v ){
                var keys = v.cat.split('/'),
                    n = obj ;        

                keys.forEach( function( k ){        
                        if ( !n[k]) {
                            n[k] = {}; 
                        }                 
                        n = n[k];                 
                }); 

                n[ v.id ] = v.id ;                
        });

        return obj;
}

and we need a function to build html

function buildHtml( obj , ul ) {

    for (i in obj) {

        var li = document.createElement('li');
        li.innerHTML = i;
        ul.appendChild( li );

        if ( typeof(obj[i])== "object" ) {

            childUl = document.createElement('ul');
            li.appendChild( childUl ); 

            buildHtml(obj[i], childUl );            
        }  

    }
} 

and make html use of input ( ie ur array )

var ul   = document.createElement('ul'),
    tree =  buildTreeObject( input ) ;

buildHtml( tree ,ul );

var div = document.createElement('div');    
div.appendChild( ul );

console.log( div.innerHTML );

Which prints desired ul li list

You can see result in http://jsfiddle.net/r3RWL/

Since you added jquery under tags, I have written solution to your problem in jQuery. Here is the code:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
    <head>
        <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
    </head>
<body>
    <div id="someDiv">
    </div>
    <script>

    function prepareNestedStructure(input) {
        var output = {},
            catLevels,
            currentCat;

        $.each(input, function(index, catObject) {
            catLevels = catObject.cat.split('/');
            currentCat = output;

            $.each(catLevels, function(index, name) {
                if(!currentCat[name])
                    currentCat[name] = {};
                currentCat = currentCat[name];
            });

            currentCat[catObject.id] = catObject.id;
        });

        return output;
    }

    function fillList(parentListEl, node) {
        $.each(node, function(key, value) {
            parentListEl.append('<li>' + key + '</li>');

            if(jQuery.type(value) === 'object') {
                var childEl = $('<ul></ul>');
                parentListEl.append(childEl);
                fillList(childEl, value);
            }
        });
    }

    var input= [
        {
            "cat": "some",
            "id": "0"
        },
        {
            "cat": "some",
            "id": "1"
        },
        {
            "cat": "some/category",
            "id": "2"
        },
        {
            "cat": "some/category/sub",
            "id": "3"
        },
        {
            "cat": "some/other",
            "id": "4"
        },
        {
            "cat": "some/thing/different",
            "id": "5"
        },
        {
            "cat": "some/thing/different",
            "id": "6"
        },
        {
            "cat": "yet/another",
            "id": "7"
        }
    ];

    var output = prepareNestedStructure(input);

    var ulDomElement = $('<ul></ul>');
    fillList(ulDomElement, output);
    $('#someDiv').append(ulDomElement);

    </script>
</body>
</html>

what about this?

transform_deeply_nested_object = (dno) ->
    result = ""
    for key, value of dno
        if value instanceof Array
            result += "<ul>"
            for elem in value
                result += "<li>" + elem + "</li>\n"
            result += "</ul>"
        else
            result += "<ul><li>" + key + "</li>"
            result += transform_deeply_nested_object value
            result += "</ul>"

Attention: Not tested!

Attention: This requires all leafs to be elements of an array always.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM