【问题标题】:Loop through JSON - special way循环 JSON - 特殊方式
【发布时间】:2019-02-13 17:38:28
【问题描述】:

我有一个非常大的嵌套 JSON 对象,像这样:

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: "..." },
      }
    }
  }
}

...我想遍历每个子树以获得这样的列表(其中每个子树的最后一个对象都有一个名为src 的全新属性,它表示对象的路径为字符串

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" },
      }
    }
  }
}

因为我真的不知道如何启动这段代码,所以到目前为止我只知道。

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; }

【问题讨论】:

  • 您要获取新对象还是更新对象?
  • @NinaScholz:原则上,只要采用所有属性并且对象具有相同的结构,这几乎没有区别。最后,两者几乎相同,只有“新”或“更新”对象将在每个分支中多获得一个属性。
  • @NinaScholz:或者您的问题是针对特定问题的?您认为更好的选择是什么?
  • 没有像 JSON 对象 这样的东西——JSON 总是 一个字符串。

标签: javascript html arrays json recursion


【解决方案1】:

我会创建一个带有标志的递归函数(此处为 leaf)来指示您是否还有更多嵌套对象。

您可以跟踪函数参数的使用位置 (path)。在每个递归步骤中,只需将键添加到路径即可。

这将改变对象,但如果你想要使用相同的技术创建一个新对象应该不难。

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)

【讨论】:

  • 这看起来像是一个功能强大且聪明的实现。谢谢你的帮助。 (+1)
【解决方案2】:

如果没有找到更多嵌套对象,您可以创建一个新对象并将路径存储为src

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; }

【讨论】:

  • 哇。我真的很惊讶。谢谢你的帮助。老实说,我还没有听说过有关 Object.assign 的任何消息。我会看看文档。 (+1)
【解决方案3】:

假设所有叶子中有一个像type 这样的公共属性可以检查该属性

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>

【讨论】:

  • 哇。这也是一个真正有效的好主意。老实说,这是我第一次接触这处房产,你做得很好。非常感谢。 (+1)
【解决方案4】:

这是一个纯函数式的实现。它不会修改输入,而是返回一个新对象,并在所有具有type 字段的对象上设置path

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]) })
        , {}
        )

在下面的浏览器中验证结果

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))

输出

{
  "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"
        }
      }
    }
  }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-07-05
    • 1970-01-01
    • 2016-05-16
    • 1970-01-01
    • 2014-03-24
    • 1970-01-01
    • 1970-01-01
    • 2019-09-26
    相关资源
    最近更新 更多