Search key in nested complex JSON
Asked Answered
I

8

8

I have to search for a key in nested JSON by JavaScript or by jQuery. In my JSON object all the keys are unique. I tried some solutions myself but they did not work. Here is my code:

json = {
    "app": [{
        "Garden": {
            "Flowers": {
                "Red flower": "Rose",
                "White Flower": "Jasmine",
                "Yellow Flower": "Marigold"
            }
        },
        "Fruits": {
            "Yellow fruit 1": "Mango",
            "Green fruit 2": "Guava",
            "White Flower 3": "groovy"
        },
        "Trees": {
            "label": {
                "Yellow fruit 2": [{"type a":"Pumpkin", "type b": "Banana",..}],
                "White Flower 2": ["Bogan 1", "Bogan 2", ...] 
            }
        }],...
    }

How can I search for a specific key in given object?

If I pass lookup(json, "type a") it should return "Pumpkin", OR If I search for "White Flower 2" it should return ["Bogan 1", "Bogan 2", ...]

Here is my try, which is not working:

function lookup(obj, k){
    for (key in obj){
        value = obj[key];
        if (k == key) return [k, value];
        if (type(value) == "Object"){
            var y = lookup(value, k);
            if (y && y[0]== k)return y;
        }
        if(type(value) == "Array"){
            for (i in value)
            {
                var x = lookup(value[i], k);
                if (x && x[0]== k)return x; 
            }
        }
        console.log(key, value);
        return null;
    } 
}

To find the type of the object, I'm using this code:

function type(object){
    var stringConstructor = "test".constructor;
    var arrayConstructor = [].constructor;
    var objectConstructor = {}.constructor;

    if (object === null) {
        return "null";
    }
    else if (object === undefined) {
        return "undefined";
    }
    else if (object.constructor === stringConstructor) {
        return "String";
    }
    else if (object.constructor === arrayConstructor) {
        return "Array";
    }
    else if (object.constructor === objectConstructor) {
        return "Object";
    }
    else {
        return "null";
    }
}
Incense answered 6/8, 2016 at 14:8 Comment(0)
R
5

You're closer than you think - moving return null; out of for (key in obj) is the main thing; otherwise, you're giving up as soon as the first key in the object doesn't match. Only give up after searching all the keys.

function lookup(obj, k) {
  for (var key in obj) {
    var value = obj[key];

    if (k == key) {
      return [k, value];
    }

    if (typeof(value) === "object" && !Array.isArray(value)) {
      var y = lookup(value, k);
      if (y && y[0] == k) return y;
    }
    if (Array.isArray(value)) {
      // for..in doesn't work the way you want on arrays in some browsers
      //
      for (var i = 0; i < value.length; ++i) {
        var x = lookup(value[i], k);
        if (x && x[0] == k) return x;
      }
    }
  }

  return null;
}


var json = {
  "app": [{
    "Garden": {
      "Flowers": {
        "Red flower": "Rose",
        "White Flower": "Jasmine",
        "Yellow Flower": "Marigold"
      }
    },
    "Fruits": {
      "Yellow fruit 1": "Mango",
      "Green fruit 2": "Guava",
      "White Flower 3": "groovy"
    },
    "Trees": {
      "label": {
        "Yellow fruit 2": [{
          "type a": "Pumpkin",
          "type b": "Banana"
        }],
        "White Flower 2": ["Bogan 1", "Bogan 2"]
      }
    }
  }]
}

function type(object) {
  var stringConstructor = "test".constructor;
  var arrayConstructor = [].constructor;
  var objectConstructor = {}.constructor;

  if (object === null) {
    return "null";
  } else if (object === undefined) {
    return "undefined";
  } else if (object.constructor === stringConstructor) {
    return "String";
  } else if (object.constructor === arrayConstructor) {
    return "Array";
  } else if (object.constructor === objectConstructor) {
    return "Object";
  } else {
    return "null";
  }
}

console.log(lookup(json, 'type a'));
console.log( lookup(json, 'White Flower 2') );

p.s. There is no such thing as a "JSON object". If it's not a string, it's not JSON. You're searching through JavaScript objects.

Refectory answered 6/8, 2016 at 14:24 Comment(0)
S
2

function lookup(obj, k) {
  if(typeof(obj) != 'object') {
    return null;
  }
  var result = null;
  if(obj.hasOwnProperty(k)) {
    return obj[k];
  } else {
    for(var o in obj) {
      result = lookup(obj[o], k);
      if(result == null) continue;
      else break;
    }
  }
  return result;
}
var json = {
  "app": [
    {
      "Garden": {
        "Flowers": {
          "Red flower": "Rose",
          "White Flower": "Jasmine",
          "Yellow Flower": "Marigold"
        }
      },
      "Fruits": {
        "Yellow fruit 1": "Mango",
        "Green fruit 2": "Guava",
        "White Flower 3": "groovy"
      },
      "Trees": {
        "label": {
          "Yellow fruit 2": [{"type a":"Pumpkin", "type b": "Banana"}],
          "White Flower 2": ["Bogan 1", "Bogan 2"] 
        }
      }
    }
  ]
}
var rs = lookup(json,'type a');
console.log(rs);
Shellishellie answered 6/8, 2016 at 15:14 Comment(0)
V
1

You can use the below code -

 var yourData = $.map(obj, function(el) { return el });  
    //Searching for the key in stored data
            var result = $.grep(yourData, function (e) {
                return e.key == your_search_key;
            });

It will return you all matches and you can access like result[0] Hope this will help you.

Vasti answered 6/8, 2016 at 14:21 Comment(7)
$.grep() is for arrays (and array-like objects); I doubt this would work as-is on an object as shown in the question.Refectory
you can JSON.parse(yourJSONObject)Vasti
No, you can JSON.parse() a string. Parsing an object will give you an error, and still won't give you an array, which is what $.grep() wants.Refectory
oh My mistake you can use var arr = $.map(obj, function(el) { return el });Vasti
#20881713Vasti
Which removes the keys (on which OP wants to search) and leaves an array of only values; and doesn't nest at all. Have you tried any of this code?Refectory
I tried this on stringify data and it is working fine for me.Vasti
A
1

This is a recursive version, written in ES6 (but to convert to ES5 you just need to replace the arrow function with a normal one and replace the consts with vars).

// We could have just returned null if there was no match, but then it wouldn't work if your data contained null.
const notFound = {};

function lookup(obj, search) {

  // Iterate over the object.
  Object.keys(obj).forEach(key => {

    // If we found the key we're looking for, return the matching value.
    if (key === search) {
      return obj[key];

    // If we've got a nested object, recursively call lookup on it.
    // If this object has the key we're looking for, return the matching value.
    } else if (obj[key].constructor === {}.constructor) {
      const result = lookup(obj[key], search);
      if (result !== notFound) return result;
    }

    // Otherwise just go onto the next iteration.

  });

  // If iterating didn't return any matching keys, return notFound.
  return notFound;

}
Antonina answered 6/8, 2016 at 15:4 Comment(0)
T
1

Not elegant but funny and performant!

function get(data, key) {
  let str = JSON.stringify(data);
  let r = new RegExp(`${key}":"([^"]{1,})"`);
  
  let res = r.exec(str);
  
  return res && res.pop() || null;
}

var data = {
  "app": [
    {
      "Garden": {
        "Flowers": {
          "Red flower": "Rose",
          "White Flower": "Jasmine",
          "Yellow Flower": "Marigold"
        }
      },
      "Fruits": {
        "Yellow fruit 1": "Mango",
        "Green fruit 2": "Guava",
        "White Flower 3": "groovy"
      },
      "Trees": {
        "label": {
          "Yellow fruit 2": [
            {
              "type a": "Pumpkin",
              "type b": "Banana"
            }
          ],
          "White Flower 2": [
            "Bogan 1",
            "Bogan 2"
          ]
        }
      }
    }
  ]
};
console.log('result', get(data, "type a"));
Tani answered 6/8, 2016 at 15:7 Comment(0)
C
0

EDIT: Question was asking for plain js or jquery, my answer uses a different library. However I'm still going to leave it as reference as it is a clean solution to the actual problem.


Original Answer

We use object-scan for all our data processing needs. Powerful once you wrap your head around how to use it and makes it easy to maintain the code. Here is how you'd answer your question

// const objectScan = require('object-scan');

const lookup = (data, term) => objectScan(['**'], {
  abort: true,
  rtn: 'value',
  filterFn: ({ property }) => property === term
})(data);

const json = { app: [{ Garden: { Flowers: { 'Red flower': 'Rose', 'White Flower': 'Jasmine', 'Yellow Flower': 'Marigold' } }, Fruits: { 'Yellow fruit 1': 'Mango', 'Green fruit 2': 'Guava', 'White Flower 3': 'groovy' }, Trees: { label: { 'Yellow fruit 2': [{ 'type a': 'Pumpkin', 'type b': 'Banana' }], 'White Flower 2': ['Bogan 1', 'Bogan 2'] } } }] };

console.log(lookup(json, "type a"));
// => Pumpkin
console.log(lookup(json, "White Flower 2"));
// => [ 'Bogan 1', 'Bogan 2' ]
.as-console-wrapper {max-height: 100% !important; top: 0}
<script src="https://bundle.run/[email protected]"></script>

Disclaimer: I'm the author of object-scan

Coffle answered 25/10, 2020 at 18:17 Comment(2)
that was plain jquery bro...!Danielson
You are right. I totally missed that. Edited the answer to clarify.Coffle
C
0

// You can use this code to get the array of object key-value, with that key //and its value Where Data is a Json object and key is the Key u want to //search

function lookup(obj, k) { let values = [];

for (var key in obj) { var value = obj[key];

  if (k == key)
    {
      values.push({key,value});
    }

    if (typeof value === "object" && !Array.isArray(value)) {
      var y = lookup(value, k);
      // values.push(y);
     values=  values.concat(y);
    }
    if (Array.isArray(value)) {
      // for..in doesn't work the way you want on arrays in some browsers
      //
      for (var i = 0; i < value.length; ++i) {
        var x = lookup(value[i], k);
        
        values=  values.concat(x);
      }
    }
  }

  return values;
}
console.log( lookup(Data, 'key') );
Controvert answered 6/4, 2023 at 19:4 Comment(1)
Your answer could be improved with additional supporting information. Please edit to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers in the help center.Downatheel
C
0

A little updated version that also looks in to array of objects and returns multiple occurences.

function lookup(obj, search, result = {}) {
  if (!result[search]) {
    result = {
      [search]: []
    };
  }
  Object.keys(obj).forEach(key => {
    if (key === search) {
      result[search].push(obj[key])
    } else if (obj[key].constructor === {}.constructor) {
      lookup(obj[key], search, result);

    } else if (obj[key].constructor === [].constructor) {
      for (const item of obj[key]) {
        lookup(item, search, result);
      }
    }
  });
  return result;
}

const testData = {
  group1: {
    id: 'group1-id',
    query: {
      name: 'getTreees',
      params: {
        id: 'tree1'
      }
    },
    fields: [
      {
        tree: 'oak',
        query: {
          name: 'getLeaves',
          params: {
            id: 'oak'
          }
        }
      },
      {
        tree: 'chestnut',
        query: {
          name: 'getLeaves',
          params: {
            id: 'chestnut'
          }
        }
      }
        
    ],
    sometOtherData: 'boo'
  }
}
console.log(lookup(testData, 'query'))
Clastic answered 26/8, 2024 at 4:10 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.