Reputation: 1387
Assuming we have the following Object, what would be the best way to iterate it up to it's end in order to get the name property for each Object? Please notice, that the size of the Object may vary and the browsing should be done in this order: a, b, a1, a2, b1, a21, b11, b12 ...
var obj = {
a: {
name: 'a',
a1: {
name: 'a1'
},
a2: {
name: 'a2',
a21: {
name: 'a21'
}
}
},
b: {
name: 'b'
b1: {
name: 'b1',
b11: {
name: 'b11'
},
b12: {
name: 'b12'
}
}
}
};
Upvotes: 0
Views: 482
Reputation: 20228
You are looking for breadth-first traversal:
// Breadh-first object traversal:
function traverse(...objs) {
for (let obj of objs) {
let next = Object.values(obj).filter(val => val && typeof val === 'object');
objs.push(...next);
}
return objs;
}
// Example:
let obj = {
a:{name:"a",a1:{name:"a1"},a2:{name:"a2",a21:{name:"a21"}}},
b:{name:"b",b1:{name:"b1",b11:{name:"b11"},b12:{name:"b12"}}}
};
for (let child of traverse(obj)) {
if (child.name) console.log(child.name);
}
Upvotes: 0
Reputation: 135197
Here's a simple recursive function to get all the name
properties in breadth-first order. I'm using a helper, pairs
, that makes it easier to process the key-value pairs provided by each object. From there, it's a simple case analysis for how the recursive function should respond:
name
, append value
to the accumulatorpairs
of value
to the list of pairs to processThis answer differs from others in that there is no side effect from running it. Instead of hard coding some behavior in the loop
function itself, loop
returns an array of name
property values that you can then do with whatever you wish.
const pairs = o =>
Object.keys(o).map(k => ({key: k, value: o[k]}))
const loop = o => {
const aux = (acc, [x,...xs]) => {
if (x === undefined)
return acc
else if (x.key === 'name')
return aux([...acc, x.value], xs)
else if (Object(x.value) === x.value)
return aux(acc, xs.concat(pairs(x.value)))
else
return aux(acc, xs)
}
return aux([], pairs(o))
}
const obj = { a: { name: 'a', a1: { name: 'a1' }, a2: { name: 'a2', a21: { name: 'a21' } } }, b: { name: 'b', b1: { name: 'b1', b11: { name: 'b11' }, b12: { name: 'b12' } } } }
console.log(loop(obj))
// [ 'a', 'b', 'a1', 'a2', 'b1', 'a21', 'b11', 'b12' ]
Alternatively, you could implement loop
using a generator such that you could act on the values while iterating. Let me know if this interests you and I'll do a write up.
Edit
Original answer processed the object in the incorrect order. The above code now answers the question properly ^_^
Upvotes: 0
Reputation: 2672
What you are looking for is a breadth-first
solution which Nina has rightly mentioned. Here is my implementation of it. In this solution, you can store the result in the array and then do console.log
later.
var obj = {
a: {
name: 'a',
a1: {
name: 'a1'
},
a2: {
name: 'a2',
a21: {
name: 'a21'
}
}
},
b: {
name: 'b',
b1: {
name: 'b1',
b11: {
name: 'b11'
},
b12: {
name: 'b12'
}
}
}
};
var ans = [];
var q = [];
q.push(obj);
function getAllKeys() {
if (q.length == 0) {
return;
}
var obj = q.shift();
var keys = Object.keys(obj);
ans = ans.concat(keys);
var index = ans.indexOf('name');
if (index != -1) {
ans.splice(index, 1);
}
for (var i = 0; i < keys.length; i++) {
if (typeof obj[keys[i]] == 'object') {
q.push(obj[keys[i]]);
}
}
getAllKeys();
}
getAllKeys();
console.log(ans);
Upvotes: 1
Reputation: 386520
You could use a breadth-first search. It is an algorithm which is iterating every level of the tree first and then the next level.
This implementation works with a queue of nodes, that means, to call the function breadthFirst
, the object/single node must be wrapped in an array.
function breadthFirst(queue) {
var newQueue = [];
queue.forEach(function (node) {
('name' in node) && console.log(node.name);
Object.keys(node).forEach(function (k) {
node[k] && typeof node[k] === 'object' && newQueue.push(node[k]);
});
});
newQueue.length && breadthFirst(newQueue);
}
var object = { a: { name: 'a', a1: { name: 'a1' }, a2: { name: 'a2', a21: { name: 'a21' } } }, b: { name: 'b', b1: { name: 'b1', b11: { name: 'b11' }, b12: { name: 'b12' } } } };
breadthFirst([object]); // a b a1 a2 b1 a21 b11 b12
.as-console-wrapper { max-height: 100% !important; top: 0; }
Upvotes: 2
Reputation:
You need a recursive thing here. You are free to change the console.log to push somewhere or whatever...
var obj = {
a: {
name: 'a',
a1: {
name: 'a1'
},
a2: {
name: 'a2',
a21: {
name: 'a21'
}
}
},
b: {
name: 'b',
b1: {
name: 'b1',
b11: {
name: 'b11'
},
b12: {
name: 'b12'
}
}
}
};
var looping = function(obj) {
var keys = Object.keys(obj);
for (var i = 0; i < keys.length; i++) {
if(typeof obj[keys[i]] === 'string') console.log(obj[keys[i]]);
else looping(obj[keys[i]]);
}
}
looping(obj);
Upvotes: 0