skip to Main Content

I have a JSON structure in the likes of

{
 "a": {"b": "value"},
 "c": {"b": "value"},
 "d": {"b": "value"}, 
 "a": {"e": {"b": ["value"]}}
}

The expected answer is 4. b could be anywhere in the tree structure.

I want to know (for testing) how many instances of b I have. Google tells me to use JSON.stringify and then work on the string. In XML, I could have used XPath to count the number of elements. Is there a JS/JSON-native way to do this other than iterating over each field’s keys?

3

Answers


  1. There is: JSON.stringify your object with a replacer function, the use of which turns it from "a string generator" into a "do whatever you want with any and all key/value pairs while walking an object tree" utility function:

    const data = {
     "a": {"b": "value"},
     "c": {"b": "value"},
     "d": {"b": "value"}, 
     "a": {"e": {"b": ["value"]}}
    };
    
    let count = 0;
    
    JSON.stringify(data, (key, value) => {
      if (key === `b`) count++;
      return value;
    });
    
    console.log(`there are ${count} bees`);

    But note that the answer will be three, not four, because there is no such thing as objects with duplicate keys in JS, and so JSON with duplicate keys follows the same rules: only the last instance of the key is real and whatever generated the JSON you’re showing needs to be fixed to not spit out (partial) nonsense =)

    Login or Signup to reply.
  2. Another solution is to recursive get all keys and then filter those to the char you’re searching for.

    const data = {
     "a": {"b": "value"},
     "c": {"b": "value"},
     "d": {"b": "value"}, 
     "x": {"e": {"b": ["value"] } }
    };
    
    const getKeys = (obj) => Object.keys(obj).reduce(
        (acc, key) => typeof obj[key] === 'object'
            ? [ ...acc, key, ...getKeys(obj[key]) ]
            : [ ...acc, key ], []
    )
    
    
    const result = getKeys(data).filter(k => k === 'b')?.length || 0;
    
    console.log(`Found ${result}x 'b'`);
    Login or Signup to reply.
  3. First things first, there is a problem with your JSON – it is invalid. An example code to recursively check for the keys like:

    function countKeys(searchKey, obj) {
     let counter = 0;
     for (const key in obj) {
        if (key === searchKey) {
            counter++;
        }
        if (typeof obj[key] === 'object') {
            counter += countKeys(searchKey, obj[key]);
        }
     }
     return counter;
    }
    

    would return 3 for "b", simply because only one of the "a" keys will be considered (the first, by my tests).

    Even if you use regex with JSON.stringify, the stringyfication only occurs with valid JSONs, which means you will also lose the last "a" key.

    console.log((JSON.stringify(keys).match(/"b":/g) || []));
    // logs Array(3) [ '"b":', '"b":', '"b":' ]
    

    Possible solutions are:

    • Make sure your backend returns a valid JSON;
    • Keep the JSON as a string (if it is a fetch response, for example, do not use .json() on it) then use the regex.

    Let me know if this helps!

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search