Youness Mourtaji
Youness Mourtaji

Reputation: 85

JavaScript recursive function to parse complex JSON object/array

I have a JSON response like this:

{
   "data":[
      {
         "type":"node--base_product_coffee",
         "id":"6dbb5a52-13ea-4f74-8af9-eb9e3ba45918",
         "date":"1990",
         "data1":[
            {
               "type1":"product_coffee1",
               "id1":"6dbb5a52-13ea-4f74-8af9-eb9e3ba45777",
               " date1 ":[
                  {
                     "  res ":"  oui "
                  },
                  {
                     "  res ":"  non "
                  }
               ]
            },
            {
               "type1":"product_coffee2",
               "id1":"6dbb5a52-13ea-4f74-8af9-eb9e3ba45666",
               "date1":[
                  {
                     "  res ":"  ouiii "
                  },
                  {
                     "  res ":"  nonnn "
                  }
               ]
            }
         ]
      }
   ]
}

My goal is to able to get value as listfrom dynamic path like data.data1.date1.res to get the result ['oui', 'non', 'ouiii', 'nonnn']

So I started by this function

parseIt = function(response, s) {
    if (!response) return null;
    if (!s) return obj;


    if (Array.isArray(response)) {
        var data = JSON.parse(JSON.stringify(response));
    } else {
        var data = JSON.parse(response);
    }

    var result = [];
    var path = [];
    
    path = s.split('.');

    if (Array.isArray(data)) {
        for (var i = 0; i < data.length; i++) {
            if (getType(data[i][path[0]]) == 'string') {
                result.push(data[i][path[0]]);
            } else {
                parseIt(data[i][path[i]], path.slice(1).join('.'));
            }

        }
    } else {
        for (var p in data) {
            if (getType(data[p]) == 'string') {
                result.push(data[p]);
            } else {
                parseIt(data[p], path.slice(1).join('.'));
            }
        }
    }
    document.writeln('result=>'+result+'</br>');
    return result;
}

document.writeln(parseIt(response2, 'data.data1.date1.res')+'</br>');

//Console Output
result=>oui,non
result=>
result=>
result=>

but I face two problems:

  1. I get Result only for the element of date1.res(which is 'oui' and 'non'), but I need all its elements (which 'oui', 'non', 'ouiii', 'nonnn')
  2. result is empty (how can store the results in a list when using recursion)

I need your help, because I need this in my work in which we have complex JSON like this.

Upvotes: 1

Views: 1685

Answers (4)

Scott Sauyet
Scott Sauyet

Reputation: 50807

A recent edit brought this question to the fore. I think this can be done much more simply.

Here's an implementation:

const makeArray = (x) => x ? Array.isArray(x) ? x : [x] : []

const _flatPath = ([p, ...ps]) => (o) =>
  p ? makeArray (o [p]) .flatMap (_flatPath (ps)): makeArray(o)

const flatPath = (path = '') => _flatPath (path.split ('.'))

const input = {data: [{type: "node--base_product_coffee", id: "6dbb5a52-13ea-4f74-8af9-eb9e3ba45918", date: "1990", data1: [{type1: "product_coffee1", id1: "6dbb5a52-13ea-4f74-8af9-eb9e3ba45777", date1: [{res: "oui"}, {res: "non"}]}, {type1: "product_coffee2", id1: "6dbb5a52-13ea-4f74-8af9-eb9e3ba45666", date1: [{res: "ouiii"}, {res: "nonnn"}]}]}]}

console .log (
  flatPath ('data.data1.date1.res') (input)
)

makeArray wraps up values in an array. If it's already an array, that's simply returned. If it's a scalar value, we return an array containing just that value. And if it nil, we return an empty array.

Our main function is _flatPath, which traverses the path (supplied as an array of strings) for the object, flattening the results into a single array.

And flatPath is the public facade to that, splitting a string like 'data.data1.date1.res' into the array ['data', 'data1', 'date1', 'res'] to pass to _flatPath.


Note that I made the assumption that the extra spaces wrapped around keys and values in the question's input were just typos. If you actually have keys that look like ' res ', but want to find them with 'res', then there's some more work to do.

Upvotes: 0

Ravikumar
Ravikumar

Reputation: 2205

You can try this by recursive function calls with array flattening.

const test={"data":[{"type":"node--base_product_coffee","id":"6dbb5a52-13ea-4f74-8af9-eb9e3ba45918","date":"1990","data1":[{"type1":"product_coffee1","id1":"6dbb5a52-13ea-4f74-8af9-eb9e3ba45777","date1":[{"res":"oui"},{"res":"non"}]},{"type1":"product_coffee2","id1":"6dbb5a52-13ea-4f74-8af9-eb9e3ba45666","date1":[{"res":"ouiii"},{"res":"nonnn"}]}]}]};
 

parseIt = function(data, [key, ...path]) {
    return (Array.isArray(data) ? data : [data]).reduce((acc, obj) => {
        if (path.length) {
            acc.push(parseIt(obj[key], path));
        } else if (obj[key]) {
            acc.push(obj[key]);
        }
        return acc;
    }, []).flat();
}

function getValue(response, s) {
    if (!response) return null;
    if (!s) return obj;
    var path = s.split('.');
    return parseIt(response, path).flat();
}

console.log(getValue(test, 'data.data1.date1.res'))

Upvotes: 2

Invizi
Invizi

Reputation: 1298

You just need to loop through data.data1

const res = data.map(elm => {
    // console.log(elm)
    return elm.data1.map(elm => {
        // console.log(elm)
        return elm.date1.map(elm => {
            // console.log(elm)
            return elm.res;
        });
    });
}).join().split(",");

console.log(res)

That should get you ["oui", "non", "ouiii", "nonnn"] left the .log's there so you don't have to type them yourself...

Upvotes: 0

Vinibr
Vinibr

Reputation: 834

I guess there is something better,

const test = {
    "data":[
       {
          "type":"node--base_product_coffee",
          "id":"6dbb5a52-13ea-4f74-8af9-eb9e3ba45918",
          "date":"1990",
          "data1":[
             {
                "type1":"product_coffee1",
                "id1":"6dbb5a52-13ea-4f74-8af9-eb9e3ba45777",
                "date1":[
                   {
                      "res":"oui"
                   },
                   {
                      "res":"non"
                   }
                ]
             },
             {
                "type1":"product_coffee2",
                "id1":"6dbb5a52-13ea-4f74-8af9-eb9e3ba45666",
                "date1":[
                   {
                      "res":"ouiii"
                   },
                   {
                      "res":"nonnn"
                   }
                ]
             }
          ]
       }
    ]
 }
 

 for(const obj of test.data){
    for(const obj2 of obj.data1){
        for(const obj3 of obj2.date1){
            console.log(obj3.res)
        }
    }
}

but yo can do something like this as well.

Upvotes: 0

Related Questions