Allan Kimaina
Allan Kimaina

Reputation: 357

I need a javascript recursive function that return an array of values when key and json object is passed to this function

I need a javascript recursive function that return an array of values when key and JavaScript object is passed to this function.

Note that the nested JavaScript object has unknown depth. The function is working fine but it returns duplicates.

   function getValuesByKey(object, key) {
    var values = [];
    recursiveFx(object);
    function recursiveFx(object) {
      for (var property in object) {
        if (object.hasOwnProperty(property)) {
          if (typeof object[property] == "object") {
           recursiveFx(object[property]);
          } else {
            //found a property which is not an object
            try {
              if (isDefined(object[key])) {
                console.log('Here is the value that is to be pushed',object[key]);
                values.push(object[key]);
              }
            } catch (e) {

            }
          }
        }
      }
    }
    return values;
  } 

Here is the isDefined helper function

function isDefined(variable) {
    try {
    if (typeof(variable) !== 'undefined') return true;
    } catch (e) {
      return false;
    }
  }

Here is an example of JavaScript object:

{
      "children": [{
        "id": "5",
        "parentid": "0",
        "text": "Device Guides",
        "index": "1",
        "children": [{
          "id": "10",
          "index": "0",
          "text": "Grandstream GXP-21XX"
        }, {
          "id": "11",
          "index": "1",
          "text": "Polycom Soundstation/Soundpoint"
        }, {
          "id": "23",
          "parentid": "8",
          "index": "2",
          "text": "New Polycom",
          "children": [{
            "id": "5",
            "parentid": "0",
            "text": "Device Guides",
            "index": "1",
            "children": [{
              "id": "10",
              "index": "0",
              "text": "Grandstream GXP-21XX"
            }, {
              "id": "11",
              "index": "1",
              "text": "Polycom Soundstation/Soundpoint"
            }, {
              "id": "23",
              "index": "2",
              "text": "New Polycom"
            }]
          }, {
            "id": "6",
            "parentid": "0",
            "text": "Pre-Sales Evaluation",
            "index": "0",
            "children": []
          }, {
            "id": "7",
            "parentid": "0",
            "text": "Router Setup Guides",
            "index": "2",
            "children": [{
              "id": "9",
              "index": "0",
              "text": "Sonicwall"
            }, {
              "id": "12",
              "index": "1",
              "text": "Cisco"
            }]
          }]
        }, {
          "id": "6",
          "parentid": "0",
          "text": "Pre-Sales Evaluation",
          "index": "0",
          "children": []
        }, {
          "id": "7",
          "parentid": "0",
          "text": "Router Setup Guides",
          "index": "2",
          "children": [{
            "id": "9",
            "index": "0",
            "text": "Sonicwall"
          }, {
            "id": "12",
            "index": "1",
            "text": "Cisco"
          }]
        }]}]};

When I run this getValuesByKey(jsonObj, 'id'); I get the following array:

["5", "5", "5", "5", "10", "10", "10", "11", "11", "11", "23", "23", "23", "23", "5", "5", "5", "5", "10", "10", "10", "11", "11", "11", "23", "23", "23", "6", "6", "6", "6", "7", "7", "7", "7", "9", "9", "9", "12", "12", "12", "6", "6", "6", "6", "7", "7", "7", "7", "9", "9", "9", "12", "12", "12"]

Notice that 5 has been returned 4 times instead of 2 times

Upvotes: 3

Views: 12471

Answers (3)

user663031
user663031

Reputation:

You are checking for the presence of key each time through the loop over the object's properties. So you are getting as many values as there are properties on the object. So:

function getValuesByKey(object, key) {
  var values = [];
  recursiveFx(object);
  function recursiveFx(object) {

    if (key in object) values.push(object[key]);
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

    for (var property in object) {
      if (object.hasOwnProperty(property)) {
        if (typeof object[property] == "object") {
         recursiveFx(object[property]);
        }
      }
    }
  }
  return values;
} 

Alternative: use JSON.stringify with replacer

Anyway, you can do this more easily with

function getValuesByKey(object, key) {
  var values = [];
  JSON.stringify(object, function(k, v) { 
    if (k === key) values.push(v);
    return v;
  });
  return values;
}

This uses the replacer parameter to JSON.stringify to intercept each key value pair. The stringified value itself we don't need and throw away.

Upvotes: 11

Nina Scholz
Nina Scholz

Reputation: 386654

Just an idea of recursion:

var data = { "children": [{ "id": "5", "parentid": "0", "text": "Device Guides", "index": "1", "children": [{ "id": "10", "index": "0", "text": "Grandstream GXP-21XX" }, { "id": "11", "index": "1", "text": "Polycom Soundstation/Soundpoint" }, { "id": "23", "parentid": "8", "index": "2", "text": "New Polycom", "children": [{ "id": "5", "parentid": "0", "text": "Device Guides", "index": "1", "children": [{ "id": "10", "index": "0", "text": "Grandstream GXP-21XX" }, { "id": "11", "index": "1", "text": "Polycom Soundstation/Soundpoint" }, { "id": "23", "index": "2", "text": "New Polycom" }] }, { "id": "6", "parentid": "0", "text": "Pre-Sales Evaluation", "index": "0", "children": [] }, { "id": "7", "parentid": "0", "text": "Router Setup Guides", "index": "2", "children": [{ "id": "9", "index": "0", "text": "Sonicwall" }, { "id": "12", "index": "1", "text": "Cisco" }] }] }, { "id": "6", "parentid": "0", "text": "Pre-Sales Evaluation", "index": "0", "children": [] }, { "id": "7", "parentid": "0", "text": "Router Setup Guides", "index": "2", "children": [{ "id": "9", "index": "0", "text": "Sonicwall" }, { "id": "12", "index": "1", "text": "Cisco" }] }] }] };

function getValuesByKey(object, key) {
    var values = [];

    function r(obj) {
        Object.keys(obj).forEach(function (k) {
            if (Array.isArray(obj[k])) {
                obj[k].forEach(r);
                return;
            }
            if (typeof obj[k] === 'object') {
                r(obj[k]);
                return;
            }
            k === key && !~values.indexOf(obj[k]) && values.push(obj[k]);
        });
    }

    r(object);
    return values;
}

document.write('<pre>' + JSON.stringify(getValuesByKey(data, 'id'), 0, 4) + '</pre>');

Upvotes: 3

Jad
Jad

Reputation: 932

Ok I got it, there is a bug in your code. You should test that key === property when testing if (isDefined(object[key])) {

Otherwise, you're just adding the matching value whenever you're examining an object that has that value in another property (if that's clear :))

Upvotes: 0

Related Questions