JSON search by key name

JavaScript performance comparison

Test case created by Evan Vosberg

Info

Compare 2 methods to get values in complex objects by key name, using different sources.

Method 1: Loop object recursive

Method 2: Search in JSON string with a lexer

Source 1: Object

Source 2: JSON string

JSON.parse and JSON.stringify are slow methods, so in some cases it's faster to search in the JSON string with a simple lexer. In this case the resulting string is probably smaller than the complete JSON string, so call of JSON.parse only for the result is faster.

Preparation code

 
<script>
Benchmark.prototype.setup = function() {
    (function (global) {
   
        var rsimple = /^(?:(?:-?(?=[1-9]|0(?!\d))\d+(?:\.\d+)?(?:[eE][+-]?\d+)?)|(?:true|false|null)|(?:"(?:[^"\\\\]*|\\\\["\\\\bfnrt\/]|\\\\u[0-9a-f]{4})*"))\s*:?\s*/,
   
                ropen = /^(?:\[|\{)\s*/,
   
                rnext = /^,\s*/,
   
                rclose = /^(?:\]|\})\s*/;
   
        function escapeRegexp(string) {
                return (string)
                        .replace(/([\.$?*|{}\(\)\[\]\\\/\+^])/g, "\\$1");
        }
   
        function lexer(json) {
                var part = "";
   
                repl = function (all) {
                        part += all;
                        return "";
                },
   
                i = 0;
   
                while (json.length && i >= 0) {
                        if (rsimple.test(json)) {
                                json = json.replace(rsimple, repl);
                        }
                        else if (ropen.test(json)) {
                                json = json.replace(ropen, repl);
                                i++;
                        }
                        else if (rclose.test(json) && i > 0) {
                                json = json.replace(rclose, repl);
                                i--;
                        }
                        else if (rnext.test(json) && i > 0) {
                                json = json.replace(rnext, repl);
                        }
                        else {
                                i = -1;
                        }
                }
   
                return part;
        }
   
        global.findInJSON = function (keyname, json, g) {
                var rfind = new RegExp("\"" + escapeRegexp(keyname) + "\"\\s*:\\s*([\\s\\S]+)$", ""),
                        results = [];
   
                function handle(json) {
                        var found = rfind.exec(json);
   
                        if (found) {
                                results.push(lexer(found[1]));
   
                                // Search global
                                if (g) {
                                        handle(found[1]);
                                }
                        }
                }
   
                // Convert to JSON and start search
                handle(json);
   
                // Get results array from JSON string
                return "[" + results.join(",") + "]";
        };
       
        global.findInObject = function (keyname, object, g) {
                var results = [];
       
                function handle(object) {
                       
                        for (var key in object) {
                                if (key === keyname) {
                                        results.push(object[key]);
                                       
                                        if (!g) {
                                                return;
                                        }
                                }
                               
                                if (typeof object[key] === "object") {
                                        handle(object[key]);
                                }
                        }
                }
       
                handle(object);
       
                return results;
        };
   
    }(this));
   
    var testDataObject = {
        "module": [{
                "exports": "Class",
                "module": "class",
                "author": "Evan Vosberg",
                "copyright": "© 2012 by Evan Vosberg",
                "info": "http://github.com/evanvosberg",
                "license": "Licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) license.",
                "class": [{
                        "name": "Class",
                        "shortdesc": "Create a class object.",
                        "category": ["Utilities", "Example"],
                        "signature": [{
                                "since": "1.0",
                                "param": [{
                                        "type": ["String"],
                                        "name": "classname",
                                        "shortdesc": "A scope name in dot natation to define the class global.",
                                        "optional": true
                                }, {
                                        "type": ["Array", "Class"],
                                        "name": "superclass",
                                        "shortdesc": "A class object or an array of class objects.",
                                        "optional": true
                                }, {
                                        "type": ["Object"],
                                        "name": "properties",
                                        "shortdesc": "An object of methods and properties for the speciefied class.",
                                        "option": [{
                                                "type": ["Function"],
                                                "name": "constructor",
                                                "shortdesc": "The class constructer which will be called on instantiate.",
                                                "since": "1.0",
                                                "optional": true
                                        }]
                                }],
                                "return": ["Class"],
                                "title": "Class([classname] [, superclass], properties)"
                        }],
                        "description": {
                                "longdesc": "<p>Javascript doesn&#39;t have a Class system like Java, <code>Class()</code> simulates this.</p><h4>Setup a constuctor for the class</h4><p>If a method of the properties named <em>constructor</em>, it will be interpreted as the constructor method oncalling a new instance of the class. Also all constuctors of superclasses will be exectuted on calling anew instance of the class.</p><h4>Call overwritten inherited methods</h4><p>If a class overwrites a method of a superclass with an own method, you can call the original method insideof the new method in the following way <code>this._super(arg1, argN)</code> or <code>this._superApply(args)</code>.</p>"
                        },
                        "method": [{
                                "name": "_proxy",
                                "shortdesc": "This method is most useful for attaching event handlers to an element where the context is the current class instance.",
                                "signature": [{
                                        "since": "1.0",
                                        "param": [{
                                                "type": ["String", "Function"],
                                                "name": "function",
                                                "shortdesc": "The function or the name of a function (from current instance) whose context will be changed to the current instance."
                                        }],
                                        "return": ["Function"],
                                        "title": "_proxy(function)"
                                }],
                                "description": {
                                        "longdesc-syntax": "md",
                                        "longdesc": "<p>Additionally, <code>._proxy()</code> makes sure that even if you bind <code>jQuery.on()</code> the function with returned from <code>._proxy()</code> it will still unbind <code>jQuery.off()</code> the correct function, if passed the original.</p>"
                                }
                        }]
                }],
                "function": [{
                        "name": "Class.isClass",
                        "shortdesc": "Determine whether the given argument is an class object.",
                        "category": ["Utilities"],
                        "signature": [{
                                "since": "1.0",
                                "param": [{
                                        "type": ["Function"],
                                        "name": "function",
                                        "shortdesc": "Object to test whether or not it is a function."
                                }, {
                                        "type": ["Boolean"],
                                        "name": "strict",
                                        "shortdesc": "A boolean indication whether the class was created with Class().",
                                        "optional": true
                                }],
                                "return": ["Boolean"],
                                "title": "Class.isClass(function [, strict])"
                        }],
                        "description": {
                                "longdesc-syntax": "md",
                                "longdesc": "<p>This method determines whether the argument is an class object.</p>"
                        }
                }]
        }]
    };
    var testDataJSON = JSON.stringify(testDataObject);
};
</script>

Test runner

Warning! For accurate results, please disable Firebug before running the tests. (Why?)

Java applet disabled.

Testing in unknown unknown
Test Ops/sec
Loop source: Object
this.findInObject("name", testDataObject, true);
pending…
Loop source: JSON (needs to convert input)
this.findInObject("name", JSON.parse(testDataJSON), true);
pending…
Lexer source: Object (needs to convert input and output)
JSON.parse(this.findInJSON("name", JSON.stringify(testDataObject), true));
pending…
Lexer source: JSON (needs to convert output)
JSON.parse(this.findInJSON("name", testDataJSON, true));
pending…

Compare results of other browsers

Revisions

You can edit these tests or add even more tests to this page by appending /edit to the URL. Here’s a list of current revisions for this page:

0 comments

Add a comment