Unflatten JS object to nested object graph

Benchmark created by Robert Koritnik on


Description

Flat object is a single level object with multi-level property names.

<span class="br0">{</span><br>&nbsp; <span class="st0">"person[0].id"</span><span class="sy0">:</span> <span class="nu0">1</span><span class="sy0">,</span><br>&nbsp; <span class="st0">"person[0].name"</span><span class="sy0">:</span> <span class="st0">"John"</span><span class="sy0">,</span><br>&nbsp; <span class="st0">"id"</span><span class="sy0">:</span> <span class="nu0">0</span><br><span class="br0">}</span>

Conversion un-flattens these properties into nested object graph

<span class="br0">{</span><br>&nbsp; <span class="st0">"id"</span><span class="sy0">:</span> <span class="nu0">0</span><span class="sy0">,</span><br>&nbsp; <span class="st0">"person"</span><span class="sy0">:</span> <span class="br0">[</span><span class="br0">{</span><br>&nbsp; &nbsp; <span class="st0">"id"</span><span class="sy0">:</span> <span class="nu0">1</span><span class="sy0">,</span><br>&nbsp; &nbsp; <span class="st0">"name"</span><span class="sy0">:</span> <span class="st0">"John"</span><br>&nbsp; <span class="br0">}</span><span class="br0">]</span><br><span class="br0">}</span>

Preparation HTML

<script>

function regExLoop(data) {
    "use strict";
    if (Object(data) !== data || Array.isArray(data))
        return data;
    var regex = /\.?([^.\[\]]+)|\[(\d+)\]/g,
        resultholder = {};
    for (var p in data) {
        var cur = resultholder,
            prop = "",
            m;
        while (m = regex.exec(p)) {
            cur = cur[prop] || (cur[prop] = (m[2] ? [] : {}));
            prop = m[2] || m[1];
        }
        cur[prop] = data[p];
    }
    return resultholder[""] || resultholder;
};

function indexOfSubstring(data) {
    "use strict";
    if (Object(data) !== data || Array.isArray(data))
        return data;
    var result = {}, cur, prop, idx, last, temp;
    for(var p in data) {
        cur = result, prop = "", last = 0;
        do {
            idx = p.indexOf(".", last);
            temp = p.substring(last, idx !== -1 ? idx : undefined);
            cur = cur[prop] || (cur[prop] = (!isNaN(parseInt(temp)) ? [] : {}));
            prop = temp;
            last = idx + 1;
        } while(idx >= 0);
        cur[prop] = data[p];
    }
    return result[""];
};

function splitStringRecursion(obj) {
    "use strict";
                var result = {};

                var generate = function(parts, value, current, index) {
                        index = index || 0;
                        current = current || result;
                        var part = parts[index++];
                        var next = parts[index];
                        //have we reached the end yet?
                        if(next !== undefined)
                        {
                                generate(parts, value, current[part] = current[part] || (isNaN(parseInt(next)) ? {} : []), index);
                        }
                        else
                        {
                                current[part] = value;
                        }
                }
                
                for(var prop in obj)
                {
                        var parts = prop.replace(/\][.[]|\[/g, ".").replace("]","").split(".");
                        generate(parts, obj[prop]);
                }
                
                return result;
        };

function splitStringLoop(obj) {
    "use strict";
                var result = {};

                for (var prop in obj)
                {
                        var parts = prop.replace(/\][.[]|\[/g, ".").replace("]","").split(".");
                        var idx = 0;
                        var current = result;
                        var part, next = 1;
                        while(next !== undefined)
                        {
                                part = parts[idx++];
                                next = parts[idx];
                                if (next !== undefined)
                                {
                                        current = current[part] = current[part] || (isNaN(next) ? {} : []);
                                }
                                else
                                {
                                        current[part] = obj[prop];
                                }
                        }
                }
                
                return result;
        };
</script>

Setup

var flatObject = {
        "related[0].related": [],
        "related[0].id": 1759807323,
        "related[0].name": "First Related name",
        "related[0].subs[0]": 1,
        "related[0].subs[1][0]": 1,
        "related[0].subs[1][1]": 2,
        "related[0].subs[2].name": "Does it work?",
        "related[0].subs[2].created": "2014-12-22T22:30:32.234Z",
        "related[0].interest.id": 1314962015,
        "related[0].interest.name": "Ideas",
        "related[0].interest.isLocked": false,
        "related[0].interest.isPrivileged": false,
        "related[0].details": "Lengthy related details that will not be sent to server",
        "related[0].author.id": 28356342,
        "related[0].author.displayName": "John Doe",
        "related[0].author.email": "john.doe@gmail.com",
        "related[0].author.isActive": true,
        "related[1].name": "Afrojack",
        "related[1].title": "Some related title",
        "related[1].details": "Another one with lengthy details",
        "related[1].id": 2345234,
        "related[1].interest.id": 1314962015,
        "related[1].interest.name": "Ideas",
        "related[1].interest.isLocked": false,
        "related[1].interest.isPrivileged": false,
        "related[1].author.id": 28356342,
        "related[1].author.displayName": "Jane Doe",
        "related[1].author.email": "jane.doe@gmail.com",
        "related[1].author.isActive": true,
        "related[1].author.spouse.id": 87962347,
        "related[1].author.spouse.displayName": "Jorge Lopez",
        "related[1].author.spouse.email": "jorge.lopez@gmail.com",
        "related[1].author.spouse.isActive": true,
        "hash": {},
        "name": "My name",
        "interest.id": 1314962015,
        "interest.name": "Ideas",
        "interest.isLocked": false,
        "interest.isPrivileged": false,
        "details": "Some lengthy details"
    };

Test runner

Ready to run.

Testing in
TestOps/sec
RegEx.match loop
regExLoop(flatObject);
ready
indexOf & substring
indexOfSubstring(flatObject);
ready
split string & recursion
splitStringRecursion(flatObject);
ready
split string & loop
splitStringLoop(flatObject);
ready

Revisions

You can edit these tests or add more tests to this page by appending /edit to the URL.

  • Revision 1: published by Robert Koritnik on