Recursively Search a Nested JSON Object

I recently ran into a situation where I wanted to be able to search a JSON object of indeterminate shape and size with arbitrary nested arrays of objects. I couldn't find anything that quite did what I wanted it to do after a quick Google search, so I wrote my own method for returning an array of results matched on the value of the criteria and property I specifed.

This is really handy method for traversing nested objects and arrays of objects with Javascript. Please note I am using UnderscoreJS to do my number/string value detection when comparing values, so either use Underscore or swap that code out.

The Code:


//Recursively traverses a JSON object searching for values that match the search property and search value
function search_object(items, search_critera, search_prop) {
    var search_results = [];

    function find_in_object(search_object, path) {
        var result = null;
        if (search_object instanceof Array) {
            for (var i = 0; i < search_object.length; i++) {
                var newPath = path.concat();
                result = find_in_object(search_object[i], newPath);
            }
        } else {
            path.push(search_object)
            for (var prop in search_object) {
                if (prop == search_prop) {
                    if ((_.isNumber(search_object[prop]) && search_object[prop] == search_critera) || (_.isString(search_object[prop]) && search_object[prop].toLowerCase() == search_critera.toLowerCase())) {
                        search_results.push(search_object);
                    }
                }
                if (search_object[prop] instanceof Object || search_object[prop] instanceof Array)
                    result = find_in_object(search_object[prop], path);
            }
        }
    }
    var last_match = find_in_object(items, []);
    return search_results;
}

Example Usage:


var json_string = [
	    {
		"id": 1,
		"title": "Not Matching Title",
		"score": 0,
		"answers": [
		    {
			"id": 1,
			"title": "Not Matching Title",
			"score": 0
		    },
		    {
			"id": 1,
			"title": "Not Matching Title",
			"score": 0,
			"answers": [
			    {
				"id": 1,
				"title": "A Matching Title",
				"score": 2
			    }
			]
		    },
		    {
			"id": 1,
			"title": "Not Matching Title",
			"score": 5
		    }
		]
	    },
	    {
		"id": 1,
		"title": "A Matching Title",
		"score": 2
	    },
	    {
		"id": 1,
		"title": "Not Matching Title",
		"score": 0,
		"answers": [
		    {
			"id": 1,
			"title": "A Matching Title",
			"score": 2,
			"answers": [
			    {
				"id": 1,
				"title": "Not Matching Title",
				"score": 7
			    },
			    {
				"id": 1,
				"title": "Not Matching Title",
				"score": 11
			    },
			    {
				"id": 1,
				"title": "A Matching Title",
				"score": 2
			    },
			    {
				"id": 1,
				"title": "Not Matching Title",
				"score": 55
			    }
			]
		    },
		    {
			"id": 1,
			"title": "A Matching Title",
			"score": 2
		    },
		    {
			"id": 1,
			"title": "Not Matching Title",
			"score": 15
		    }
		]
	    }
	]

function search_object(items, search_critera, search_prop) {
    var search_results = [];

    function find_in_object(search_object, path) {
        var result = null;
        if (search_object instanceof Array) {
            for (var i = 0; i < search_object.length; i++) {
                var newPath = path.concat();
                result = find_in_object(search_object[i], newPath);
            }
        } else {
            path.push(search_object)
            for (var prop in search_object) {
                if (prop == search_prop) {
                    if ((_.isNumber(search_object[prop]) && search_object[prop] == search_critera) || (_.isString(search_object[prop]) && search_object[prop].toLowerCase() == search_critera.toLowerCase())) {
                        search_results.push(search_object);
                    }
                }
                if (search_object[prop] instanceof Object || search_object[prop] instanceof Array)
                    result = find_in_object(search_object[prop], path);
            }
        }
    }
    var last_match = find_in_object(items, []);
    return search_results;
}

var matched_results = search_object(json_string, 2, "score");
console.log(matched_results)

This code will match the score key where the value is equal to 2 (note, it will also return the all nested arrays if the parent matches as well as the child):


[
    {
        "id": 1,
        "title": "A Matching Title",
        "score": 2
    },
    {
        "id": 1,
        "title": "A Matching Title",
        "score": 2
    },
    {
        "id": 1,
        "title": "A Matching Title",
        "score": 2,
        "answers": [
            {
                "id": 1,
                "title": "Not Matching Title",
                "score": 7
            },
            {
                "id": 1,
                "title": "Not Matching Title",
                "score": 11
            },
            {
                "id": 1,
                "title": "A Matching Title",
                "score": 2
            },
            {
                "id": 1,
                "title": "Not Matching Title",
                "score": 55
            }
        ]
    },
    {
        "id": 1,
        "title": "A Matching Title",
        "score": 2
    },
    {
        "id": 1,
        "title": "A Matching Title",
        "score": 2
    }
]

Hopefully this should be useful for anyone wanting to search nested JSON objects based on a key value.

Comments

There are no comments

Post a comment

The red asterisk (*) indicates a mandatory field. Please ensure these fields are completed.