Fastest way to flatten / un-flatten nested JSON objects

前端 未结 13 1289
孤城傲影
孤城傲影 2020-11-21 20:32

I threw some code together to flatten and un-flatten complex/nested JSON objects. It works, but it\'s a bit slow (triggers the \'long script\' warning).

For the flat

13条回答
  •  说谎
    说谎 (楼主)
    2020-11-21 21:20

    I wrote two functions to flatten and unflatten a JSON object.


    Flatten a JSON object:

    var flatten = (function (isArray, wrapped) {
        return function (table) {
            return reduce("", {}, table);
        };
    
        function reduce(path, accumulator, table) {
            if (isArray(table)) {
                var length = table.length;
    
                if (length) {
                    var index = 0;
    
                    while (index < length) {
                        var property = path + "[" + index + "]", item = table[index++];
                        if (wrapped(item) !== item) accumulator[property] = item;
                        else reduce(property, accumulator, item);
                    }
                } else accumulator[path] = table;
            } else {
                var empty = true;
    
                if (path) {
                    for (var property in table) {
                        var item = table[property], property = path + "." + property, empty = false;
                        if (wrapped(item) !== item) accumulator[property] = item;
                        else reduce(property, accumulator, item);
                    }
                } else {
                    for (var property in table) {
                        var item = table[property], empty = false;
                        if (wrapped(item) !== item) accumulator[property] = item;
                        else reduce(property, accumulator, item);
                    }
                }
    
                if (empty) accumulator[path] = table;
            }
    
            return accumulator;
        }
    }(Array.isArray, Object));
    

    Performance:

    1. It's faster than the current solution in Opera. The current solution is 26% slower in Opera.
    2. It's faster than the current solution in Firefox. The current solution is 9% slower in Firefox.
    3. It's faster than the current solution in Chrome. The current solution is 29% slower in Chrome.

    Unflatten a JSON object:

    function unflatten(table) {
        var result = {};
    
        for (var path in table) {
            var cursor = result, length = path.length, property = "", index = 0;
    
            while (index < length) {
                var char = path.charAt(index);
    
                if (char === "[") {
                    var start = index + 1,
                        end = path.indexOf("]", start),
                        cursor = cursor[property] = cursor[property] || [],
                        property = path.slice(start, end),
                        index = end + 1;
                } else {
                    var cursor = cursor[property] = cursor[property] || {},
                        start = char === "." ? index + 1 : index,
                        bracket = path.indexOf("[", start),
                        dot = path.indexOf(".", start);
    
                    if (bracket < 0 && dot < 0) var end = index = length;
                    else if (bracket < 0) var end = index = dot;
                    else if (dot < 0) var end = index = bracket;
                    else var end = index = bracket < dot ? bracket : dot;
    
                    var property = path.slice(start, end);
                }
            }
    
            cursor[property] = table[path];
        }
    
        return result[""];
    }
    

    Performance:

    1. It's faster than the current solution in Opera. The current solution is 5% slower in Opera.
    2. It's slower than the current solution in Firefox. My solution is 26% slower in Firefox.
    3. It's slower than the current solution in Chrome. My solution is 6% slower in Chrome.

    Flatten and unflatten a JSON object:

    Overall my solution performs either equally well or even better than the current solution.

    Performance:

    1. It's faster than the current solution in Opera. The current solution is 21% slower in Opera.
    2. It's as fast as the current solution in Firefox.
    3. It's faster than the current solution in Firefox. The current solution is 20% slower in Chrome.

    Output format:

    A flattened object uses the dot notation for object properties and the bracket notation for array indices:

    1. {foo:{bar:false}} => {"foo.bar":false}
    2. {a:[{b:["c","d"]}]} => {"a[0].b[0]":"c","a[0].b[1]":"d"}
    3. [1,[2,[3,4],5],6] => {"[0]":1,"[1][0]":2,"[1][1][0]":3,"[1][1][1]":4,"[1][2]":5,"[2]":6}

    In my opinion this format is better than only using the dot notation:

    1. {foo:{bar:false}} => {"foo.bar":false}
    2. {a:[{b:["c","d"]}]} => {"a.0.b.0":"c","a.0.b.1":"d"}
    3. [1,[2,[3,4],5],6] => {"0":1,"1.0":2,"1.1.0":3,"1.1.1":4,"1.2":5,"2":6}

    Advantages:

    1. Flattening an object is faster than the current solution.
    2. Flattening and unflattening an object is as fast as or faster than the current solution.
    3. Flattened objects use both the dot notation and the bracket notation for readability.

    Disadvantages:

    1. Unflattening an object is slower than the current solution in most (but not all) cases.

    The current JSFiddle demo gave the following values as output:

    Nested : 132175 : 63
    Flattened : 132175 : 564
    Nested : 132175 : 54
    Flattened : 132175 : 508
    

    My updated JSFiddle demo gave the following values as output:

    Nested : 132175 : 59
    Flattened : 132175 : 514
    Nested : 132175 : 60
    Flattened : 132175 : 451
    

    I'm not really sure what that means, so I'll stick with the jsPerf results. After all jsPerf is a performance benchmarking utility. JSFiddle is not.

提交回复
热议问题