Obtuve un objeto JSON anidado realmente grande como este:

let myData = {
  character: {
    player: {
      player_1: { type: "player1", test:"A" },
      player_2: { type: "player2", test:"B" },
      player_3: { type: "player3", test:"C" }
    },
    enemy: {
      enemy_walk: {
        enemy_walk_1: { type:"enemy_walkA", test: "..." },
        enemy_walk_2: { type:"enemy_walkB", test: "..." },
        enemy_walk_3: { type:"enemy_walkY", test: "..." }
      }
    }
  },
  blocks: {
    wall: {
      wall_1: { type:"wallA", test: "..." },
      wall_2: { type:"wallB", test: "..." },
    },
    obstacle: {
      brick: {
        brick1: { type:"brickA", test: "..." },
        brick2: { type:"brickC", test: "..." },
      }
    }
  }
}

... y me gustaría recorrer cada subárbol para obtener una lista como esta (donde el último objeto de cada subárbol obtiene una nueva propiedad llamada src que representa la ruta del objeto como una cadena :

let result = {
  character: {
    player: {
      player_1: { type: "player1", test:"A", src: "character/player/player_1" },
      player_2: { type: "player2", test:"B", src: "character/player/player_2" },
      player_3: { type: "player3", test:"C", src: "character/player/player_3" }
    },
    enemy: {
      enemy_walk: {
        enemy_walk_1: { type:"enemy_walkA", test: "...", src: "character/enemy/enemy_walk_1" },
        enemy_walk_2: { type:"enemy_walkB", test: "...", src: "character/enemy/enemy_walk_2" },
        enemy_walk_3: { type:"enemy_walkY", test: "...", src: "character/enemy/enemy_walk_3" }
      }
    }
  },
  blocks: {
    wall: {
      wall_1: { type:"wallA", test: "...", src: "blocks/wall/wall_1" },
      wall_2: { type:"wallB", test: "...", src: "blocks/wall/wall_2" },
    },
    obstacle: {
      brick: {
        brick1: { type:"brickA", test: "...", src: "blocks/obstacle/brick/brick1" },
        brick2: { type:"brickC", test: "...", src: "blocks/obstacle/brick/brick2" },
      }
    }
  }
}

Porque realmente no tengo idea de cómo iniciar este código es todo lo que tengo hasta ahora.

var myData={character:{player:{player_1:{type:"player1",test:"A"},player_2:{type:"player2",test:"B"},player_3:{type:"player3",test:"C"}},enemy:{enemy_walk:{enemy_walk_1:{type:"enemy_walkA",test:"..."},enemy_walk_2:{type:"enemy_walkB",test:"..."},enemy_walk_3:{type:"enemy_walkY",test:"..."}}}},blocks:{wall:{wall_1:{type:"wallA",test:"..."},wall_2:{type:"wallB",test:"..."}},obstacle:{brick:{brick1:{type:"brickA",test:"..."},brick2:{type:"brickC",test:"..."}}}}};

let updateSRC = function(data) {
  let _data = data;
  let recursive = function(_data) {
    for (let key in _data) {
      if (typeof _data[key] == "Object") {
        recursive(_data[key]);
      } else {
         _data[key].src = "?"
      }
    }; recursive(_data)
  }; return _data || null;
}
let result = updateSRC(myData);
console.log(result)
.as-console-wrapper { max-height: 100% !important; top: 0; }
3
user3596335 8 sep. 2018 a las 21:32

4 respuestas

La mejor respuesta

Simplemente haría una función recursiva con una bandera (aquí leaf) para indicar si tiene más objetos anidados.

Puede realizar un seguimiento de dónde está utilizando un parámetro para la función (path). Con cada paso recursivo, solo agregue la clave a la ruta.

Esto alterará el objeto en su lugar, pero no debería ser difícil crear un nuevo objeto usando la misma técnica si eso es lo que buscas.

let myData = {character: {player: {player_1: { type: "player1", test:"A" },player_2: { type: "player2", test:"B" },player_3: { type: "player3", test:"C" }},enemy: {enemy_walk: {enemy_walk_1: { type:"enemy_walkA", test: "..." },enemy_walk_2: { type:"enemy_walkB", test: "..." },enemy_walk_3: { type:"enemy_walkY", test: "..." }}}},blocks: {wall: {wall_1: { type:"wallA", test: "..." },wall_2: { type:"wallB", test: "..." },},obstacle: {brick: {brick1: { type:"brickA", test: "..." },brick2: { type:"brickC", test: "..." },}}}}

function walk(obj, path=''){
    let leaf = true
    Object.keys(obj).forEach(key => {
        if (typeof obj[key] === 'object' ){
            walk(obj[key], path +  '/' + key)
            leaf = false // this object has children, so don't add `src` prop
        }
    })
    if (leaf) obj['src'] = path
}

walk(myData)
console.log(myData)
2
Mark Meyer 8 sep. 2018 a las 18:49

Suponiendo que una propiedad común como type en todas las hojas puede verificar esa propiedad

const setPaths = (o, path = '') => {
  Object.keys(o).forEach(k => {
    const currPath = path ? `${path}/${k}` : k;
    if ('type' in o[k]) {
       o[k].src = currPath;
    } else {     
      setPaths(o[k], currPath)
    }
  })
}
setPaths(myData)

console.log(myData)
<script>
  let myData = {
    character: {
      player: {
        player_1: {
          type: "player1",
          test: "A"
        },
        player_2: {
          type: "player2",
          test: "B"
        },
        player_3: {
          type: "player3",
          test: "C"
        }
      },
      enemy: {
        enemy_walk: {
          enemy_walk_1: {
            type: "enemy_walkA",
            test: "..."
          },
          enemy_walk_2: {
            type: "enemy_walkB",
            test: "..."
          },
          enemy_walk_3: {
            type: "enemy_walkY",
            test: "..."
          }
        }
      }
    },
    blocks: {
      wall: {
        wall_1: {
          type: "wallA",
          test: "..."
        },
        wall_2: {
          type: "wallB",
          test: "..."
        },
      },
      obstacle: {
        brick: {
          brick1: {
            type: "brickA",
            test: "..."
          },
          brick2: {
            type: "brickC",
            test: "..."
          },
        }
      }
    }
  }
</script>
0
charlietfl 8 sep. 2018 a las 19:22

Puede crear un nuevo objeto y almacenar la ruta como src si no se encuentran más objetos anidados.

function getUpdate(object, path = []) {
    return Object.assign(...Object
        .entries(object)
        .map(([k, v]) => v && typeof v === 'object'
            ? { [k]: getUpdate(v, path.concat(k)) }
            : { [k]: v, src: path.join('/') }
        )
    );
}

var object = { character: { player: { player_1: { type: "player1", test: "A" }, player_2: { type: "player2", test: "B" }, player_3: { type: "player3", test: "C" } }, enemy: { enemy_walk: { enemy_walk_1: { type: "enemy_walkA", test: "..." }, enemy_walk_2: { type: "enemy_walkB", test: "..." }, enemy_walk_3: { type: "enemy_walkY", test: "..." } } } }, blocks: { wall: { wall_1: { type: "wallA", test: "..." }, wall_2: { type: "wallB", test: "..." } }, obstacle: { brick: { brick1: { type: "brickA", test: "..." }, brick2: { type: "brickC", test: "..." } } } } };

console.log(getUpdate(object));
.as-console-wrapper { max-height: 100% !important; top: 0; }
1
Nina Scholz 8 sep. 2018 a las 19:01

Aquí hay una implementación funcional pura. No modifica la entrada y en su lugar devuelve un nuevo objeto con path establecido en todos los objetos que tienen un campo type.

const addPath = (o = {}, path = []) =>
  // non-object? return input
  Object (o) !== o
    ? o

  // object has 'type'? assign 'path'
  : o.type !== undefined
    ? { ...o, path: [...path, o.type] .join ('/') }

  // otherwise: recur on each value with updated path
  : Object
      .entries (o)
      .reduce
        ( (acc, [key, value]) =>
            ({ ...acc, [key]: addPath (value, [...path, key]) })
        , {}
        )

Verifique el resultado en su navegador a continuación

const addPath = (o = {}, path = []) =>
  Object (o) !== o
    ? o
  : o.type !== undefined
    ? { ...o, path: [...path, o.type] .join ('/') }
  : Object
      .entries (o)
      .reduce
        ( (acc, [key, value]) =>
            ({ ...acc, [key]: addPath (value, [...path, key]) })
        , {}
        )
        
let myData = {
  character: {
    player: {
      player_1: { type: "player1", test:"A" },
      player_2: { type: "player2", test:"B" },
      player_3: { type: "player3", test:"C" }
    },
    enemy: {
      enemy_walk: {
        enemy_walk_1: { type:"enemy_walkA", test: "..." },
        enemy_walk_2: { type:"enemy_walkB", test: "..." },
        enemy_walk_3: { type:"enemy_walkY", test: "..." }
      }
    }
  },
  blocks: {
    wall: {
      wall_1: { type:"wallA", test: "..." },
      wall_2: { type:"wallB", test: "..." },
    },
    obstacle: {
      brick: {
        brick1: { type:"brickA", test: "..." },
        brick2: { type:"brickC", test: "..." },
      }
    }
  }
}

console.log (addPath (myData))

Salida

{
  "character": {
    "player": {
      "player_1": {
        "type": "player1",
        "test": "A",
        "path": "character/player/player_1/player1"
      },
      "player_2": {
        "type": "player2",
        "test": "B",
        "path": "character/player/player_2/player2"
      },
      "player_3": {
        "type": "player3",
        "test": "C",
        "path": "character/player/player_3/player3"
      }
    },
    "enemy": {
      "enemy_walk": {
        "enemy_walk_1": {
          "type": "enemy_walkA",
          "test": "...",
          "path": "character/enemy/enemy_walk/enemy_walk_1/enemy_walkA"
        },
        "enemy_walk_2": {
          "type": "enemy_walkB",
          "test": "...",
          "path": "character/enemy/enemy_walk/enemy_walk_2/enemy_walkB"
        },
        "enemy_walk_3": {
          "type": "enemy_walkY",
          "test": "...",
          "path": "character/enemy/enemy_walk/enemy_walk_3/enemy_walkY"
        }
      }
    }
  },
  "blocks": {
    "wall": {
      "wall_1": {
        "type": "wallA",
        "test": "...",
        "path": "blocks/wall/wall_1/wallA"
      },
      "wall_2": {
        "type": "wallB",
        "test": "...",
        "path": "blocks/wall/wall_2/wallB"
      }
    },
    "obstacle": {
      "brick": {
        "brick1": {
          "type": "brickA",
          "test": "...",
          "path": "blocks/obstacle/brick/brick1/brickA"
        },
        "brick2": {
          "type": "brickC",
          "test": "...",
          "path": "blocks/obstacle/brick/brick2/brickC"
        }
      }
    }
  }
}
0
Thank you 8 sep. 2018 a las 21:06