Reputation: 498
Assuming I have this deeply nested object
let obj = {
comp: "el",
type: "ele",
children: [
{
children: [
{
text: "text",
children: [],
uid: "-5zIANJjCz-qU0MNcf5E1",
},
{
text:"text",
children: [],
uid: "bpeVj4NaD_Io3WP7V0v91"
}
],
uid: "ZygtUliVsFbFpbmHv5yp9"
},
{
children: [
{
text: "text",
children: [],
uid: "qmw59v1BVRMEuM8r9VvQp",
},
{
text: "text. ",
children: [],
uid: "q-QeWlEnKvVTjD0dPRXCu"
}
],
uid: "rp_gURLFIgd1n7bn-pRhn"
},
{
children: [
{
text: "text",
children: [],
uid: "8gIytZ52tq0mqiVAhAJLN",
},
{
text: "text",
children: [],
uid: "q-QeeWlEnKvVTjD0dPRXCu"
}
],
uid: "M3hqA-Rp1wQz60IVdIGUX"
}
],
uid: "M4JBL9SoOOLo5iQSb5M_P"
};
How would I go about getting an array of all uid values?
Currently I am using object scan
https://github.com/blackflux/object-scan
const find = (data, needle) => objectScan([needle], { rtn: "value" })(data);
const allIds = find(obj, "**.uid");
This works really well but I am curious how this could be achieved with recursion.
Upvotes: 1
Views: 877
Reputation: 71451
You can use a recursive generator function:
function* get_uid(d){
yield* ('uid' in d ? [d.uid] : [])
for (var c of ('children' in d ? d.children : [])){
yield* get_uid(c)
}
}
let obj = {'comp': 'el', 'type': 'ele', 'children': [{'children': [{'text': 'text', 'children': [], 'uid': '-5zIANJjCz-qU0MNcf5E1'}, {'text': 'text', 'children': [], 'uid': 'bpeVj4NaD_Io3WP7V0v91'}], 'uid': 'ZygtUliVsFbFpbmHv5yp9'}, {'children': [{'text': 'text', 'children': [], 'uid': 'qmw59v1BVRMEuM8r9VvQp'}, {'text': 'text. ', 'children': [], 'uid': 'q-QeWlEnKvVTjD0dPRXCu'}], 'uid': 'rp_gURLFIgd1n7bn-pRhn'}, {'children': [{'text': 'text', 'children': [], 'uid': '8gIytZ52tq0mqiVAhAJLN'}, {'text': 'text', 'children': [], 'uid': 'q-QeeWlEnKvVTjD0dPRXCu'}], 'uid': 'M3hqA-Rp1wQz60IVdIGUX'}], 'uid': 'M4JBL9SoOOLo5iQSb5M_P'}
var result = [...get_uid(obj)]
console.log(result);
Upvotes: 0
Reputation: 50797
It's pretty easy with a simple helper function that gathers results paired with one that uses that to get the id of every node visited::
const gather = (fn) => (o) =>
[fn (o), ... (o .children || []) .flatMap (gather (fn))]
const obj = {comp: "el", type: "ele", children: [{children: [{text: "text", children: [], uid: "-5zIANJjCz-qU0MNcf5E1"}, {text: "text", children: [], uid: "bpeVj4NaD_Io3WP7V0v91"}], uid: "ZygtUliVsFbFpbmHv5yp9"}, {children: [{text: "text", children: [], uid: "qmw59v1BVRMEuM8r9VvQp"}, {text: "text. ", children: [], uid: "q-QeWlEnKvVTjD0dPRXCu"}], uid: "rp_gURLFIgd1n7bn-pRhn"}, {children: [{text: "text", children: [], uid: "8gIytZ52tq0mqiVAhAJLN"}, {text: "text", children: [], uid: "q-QeeWlEnKvVTjD0dPRXCu"}], uid: "M3hqA-Rp1wQz60IVdIGUX"}], uid: "M4JBL9SoOOLo5iQSb5M_P"}
console .log (gather (x => x .uid) (obj))
// or, storing the uid function in a helper:
// const extractUids = gather (x => x .uid)
// console .log (extractUids (obj))
But if you have no other need for the generic solution, you can alternatively inline that helper directly into the function:
const extractUids = (o) =>
[o .uid, ... (o .children || []) .flatMap (extractUids)]
Upvotes: 3
Reputation: 1372
For trees (and graphs without cycles) you can use simple recursion-based strategy as follows:
function reduceTree<Item, Acc>(
next: (item: Item) => Item[],
reducer: (acc: Acc, item: Item) => Acc
) {
const step = (acc: Acc, item: Item): Acc => next(item)
.reduce((_, child) => step(_, child), reducer(acc, item));
return step;
}
// Test
const allIds = reduceTree(
(item: TreeItem) => item.children,
(allIds: string[], item) => [...allIds, item.uid]
)(
[], obj
);
Upvotes: 0