简体   繁体   中英

Traversing an object in javascript and convert it in specific format

I have the following object structure:

{
    a: {
        b: "abc",
        c: [
            {
                d: "pqr",
                e: true
            },
            {
                f: 1,
                e: ["xyz", 1]
            }
        ]
    }
}

Now I want to convert these structure in specific format that are shown in expected output.

I'm trying to traverse above structure but the constructed path not correct Here is what I'm doing

const getFinalNode = function(data, path){
  if (typeof data === 'object'){
    for (let key in data) {
      // console.log(key);
      if (typeof data[key] === 'object'){
        path += key+'/'
        getFinalNode(data[key], path)
        // console.log(true);
      }else{
        console.log({
          value: data[key],
          key: key,
          path:path
        })
      }
    }
  }
}
getFinalNode(data, ""); 

My expected output should be

[
    {
        name: "b",
        value: "abc",
        path: "a/b"
    },
    {
        name: "d",
        value: "pqr",
        path: "a/c/0/d"
    },
    {
        name: "e",
        value: true,
        path: "a/c/0/e"
    },
    {
        name: "f",
        value: 1,
        path: "a/c/1/f"
    },
    {
        name: "e",
        value: 'xyz',
        path: "a/c/1/e"
    },
    {
        name: "e",
        value: 1,
        path: "a/c/1/e"
    }
]

So how can I traverse above structure and convert it in expected output or is there any solution that can I use in my existing code.

Now I getting following output

在此处输入图片说明

You should pass both the path and the accumulated array acc to the recursive call of getFinalNode :

 const getFinalNode = function(data, acc = [], path = "") { // default values for acc and path, so we won't bother ourselves with that for(let key in data) { // for each key in the object/array data if(data.hasOwnProperty(key)) { // if this is an own property if(data[key] && typeof(data[key]) === "object") { // if the value of the current property is an object/array (not null: typeof null === "object") getFinalNode(data[key], acc, path + "/" + key); // then traverse it, passing acc to add to what we already have } else { // otherwise acc.push({ // make a leaf object and push it to the accumulator acc path: path.slice(1) + "/" + key, // slice to remove the first "/" from path name: key, value: data[key] }); } } } return acc; } let data = {"a":{"b":"abc","c":[{"d":"pqr","e":true},{"f":1,"e":["xyz",1]}]}}; let result = getFinalNode(data); console.log(result); 

You mostly just needed to pass an array through and give a little adjustment to the path you're building.

The inclusion of the last item in the final path is a little surprising, and took some extra tweaking, but I think it's what you're looking for in the result.

 const getFinalNode = function(data, path, result){ result = result || []; // This check isn't needed if the initial call will always be an object. if (typeof data === 'object'){ for (let [key, val] of Object.entries(data)) { if (typeof val === 'object'){ getFinalNode(val, path ? (path + "/" + key) : key, result); }else{ const notNum = isNaN(key); result.push({ name: notNum ? key : path.slice(path.lastIndexOf("/") + 1), value: val, path:path + (notNum ? "/" + key : "") }); } } } return result; } const data = { a: { b: "abc", c: [ { d: "pqr", e: true }, { f: 1, e: ["xyz", 1] } ] } }; console.log(getFinalNode(data, "")); 

I used Object.entries() with destructuring assignment in a for-of just for the convenience of having the key and value assigned to variables. Your original for-in loop was fine too.

there are two things you are missing to get the expected output: - You are not adding the path when data["key"] is not an object - You are looking for get an array output but you are printing every element separatedly.

Looking at you desired output i see the path always includes b, even when b and c are siblings, and that doesn´t allow have them both in the same path.Taking this as a typing mistake, I modified a bit the script in order to get the correct output:

 var data = {a: { b: "abc", c: [ { d: "pqr", e: true},{ f: 1, e: ["xyz", 1] }]}}; var output = []; getFinalNode = function(data, path){ if (typeof data === 'object'){ for (let key in data) { // console.log(key); path.push(key) getFinalNode(data[key], path) // console.log(true); if (typeof data[key] != 'object'){ output.push({ value: data[key], key: key, path:path.join("/") });} } } path.splice(-1,1) } getFinalNode(data, []); console.log(output); 

For fun, dirty solution, kids don't do that at home !

 function getFinalNode(node) { return (function rec(obj, path='', results=[]) { path = path && (path + '/') return Object.entries(obj) .reduce((arr, [name, value]) => void (typeof value === 'object' ? rec(value, path + name, arr) : arr.push({ name, value, path: path + name })) || arr, results) })(node) } console.log(getFinalNode({a:{b:"abc",c:[{d:"pqr",e:true},{f:1,e:["xyz",1]}]}})) 

To explain a bit:

path = path && (path + '/')

If path is empty, the first part of the condition will be evaluated, so will get an empty string. Otherwise, the second part is evaluated, and we do the concatenation.

void (whatEverExpression) || arr

void will make whatEverExpression returning undefined, so arr will be returned instead.

(It's a trick, I find handful when reducing something. Even if ESLint forbid the use of void :D)


Edit: Inspired by rock star's answer, with entries , it's even better :D

You could take a function for collecting the items and a function for a recursive call for iterating the object.

 function traverse(object) { function iter(o, p) { Object.keys(o).forEach(function (k) { if (o[k] && typeof o[k] === 'object') { return iter(o[k], p.concat(k)); } result.push({ name: k, value: o[k], path: p.join('/') }); }); } var result = []; iter(object, []); return result; } var object = { a: { b: "abc", c: [{ d: "pqr", e: true }, { f: 1, e: ["xyz", 1] }] } }; console.log(traverse(object)); 
 .as-console-wrapper { max-height: 100% !important; top: 0; } 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM