Reputation:
I got a really big nested JSON object like this one:
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: "..." },
}
}
}
}
... and I'd like to loop through every subtree to get a list like this (where the last object of every subtree gets a brand new property called src
that represents the path of the object as string:
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" },
}
}
}
}
Because I do not really got a clue how to start this code is all that I've got so far.
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; }
Upvotes: 3
Views: 93
Reputation: 135197
Here's a pure functional implementation. It does not modify the input and instead returns a new object with path
set on all objects that have a type
field.
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]) })
, {}
)
Verify the result in your browser below
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))
Output
{
"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"
}
}
}
}
}
Upvotes: 0
Reputation: 171679
Assuming a common property like type
in all leaves can check for that property
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>
Upvotes: 0
Reputation: 386560
You could create a new object and store the path as src
if no further nested object is found.
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; }
Upvotes: 1
Reputation: 92440
I would just make a recursive function with a flag (here leaf
) to indicate whether the you have any more nested objects.
You can keep track of where you are using a parameter to the function(path
). With each recursive step, just add the key to the path.
This will alter the object in place, but it shouldn't be hard to create a new object using the same technique if that's what you're after.
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)
Upvotes: 2