'Parsing recursive json, from markdown AST
Recursive functions are not my forté, and im pretty sure thats what is needed here.
I have a nested json object, which represents a nested checklist It is autogenerated from markdown using mdast-util-gfm-task-list-item
* AAA
* BBB
* CCC
* [x] DDD
* [ ] EEE
* FFF
{
"type": "root",
"children": [
{
"type": "list",
"ordered": false,
"start": null,
"spread": false,
"children": [
{
"type": "listItem",
"spread": false,
"checked": null,
"children": [
{
"type": "paragraph",
"children": [
{
"type": "text",
"value": "AAA"
}
]
},
{
"type": "list",
"ordered": false,
"start": null,
"spread": false,
"children": [
{
"type": "listItem",
"spread": false,
"checked": null,
"children": [
{
"type": "paragraph",
"children": [
{
"type": "text",
"value": "BBB"
}
]
}
]
}
]
}
]
},
{
"type": "listItem",
"spread": false,
"checked": null,
"children": [
{
"type": "paragraph",
"children": [
{
"type": "text",
"value": "CCC"
}
]
},
{
"type": "list",
"ordered": false,
"start": null,
"spread": false,
"children": [
{
"type": "listItem",
"spread": false,
"checked": null,
"children": [
{
"type": "paragraph",
"children": [
{
"type": "text",
"value": "DDD"
}
]
},
{
"type": "list",
"ordered": false,
"start": null,
"spread": false,
"children": [
{
"type": "listItem",
"spread": false,
"checked": null,
"children": [
{
"type": "paragraph",
"children": [
{
"type": "text",
"value": "EEE"
}
]
}
]
},
{
"type": "listItem",
"spread": false,
"checked": null,
"children": [
{
"type": "paragraph",
"children": [
{
"type": "text",
"value": "FFF"
}
]
}
]
}
]
}
]
}
]
}
]
}
]
}
]
}
I need to manipulate it to be in the following, more consise format:
{
"type" : "list",
"data" : {
"style" : "unordered",
"items" : [
{
"content": "AAA",
"checked" : null
"items": [
{
"content": "BBB",
"checked" : null
"items": []
}
]
},
{
"content": "CCC",
"checked" : null
"items": [
{
"content": "DDD",
"checked": true,
"items": [
{
"content": "EEE",
"checked": false,
"items": []
},
{
"content": "FFF",
"checked": null,
"items": []
}
]
},
]
}
]
}
}
Any help greatly appreciated - I have been banging my head against the wall for a couple of hours now.
Solution 1:[1]
I think I am pretty close here but haven't been able to finish it off:
function transform(data) {
const {type} = data;
if (type === "root") {
return transform(data.children[0]);
} else if (type === "list") {
return {
type,
data: {
style: data.ordered ? "ordered" : "unordered",
items: data.children.map(child => transform(child))
}
}
} else if (type === "listItem") {
return {
type,
checked: data.checked,
content: data.children[0].children[0].value,
items: data.children.filter(child => child.type === "list").map(child => transform(child))
}
}
return data;
}
// Pass your data in here as fromData
const toData = transform(fromData);
// toData would then be the new format
Solution 2:[2]
Thanks @jimTheDev
I also recived some assistance on another forum, and both answers were useful. In the end, I have sucssfully produced the following function, which works almost perfectly for my use case.
let ast_to_ejs = (input) => {
const { type, children } = input;
if (type === "root") {
const data = {};
data.style = input.ordered ? 'ordered' : 'unordered';
data.items = children.map(ast_to_ejs);
return { type: 'list', data };
} else if (type === 'list') {
return children.map(ast_to_ejs);
} else if (type === 'listItem') {
const checked = input.checked;
const content = children[0].children[0].value;
const items = children[1] ? ast_to_ejs(children[1]) : [];
return { content, checked, items };
}
};
Solution 3:[3]
The version I had been working on is similar to what you figured out yourself, but it's done in a different coding style, and it removes one layer of array from /root/data/items, which wasn't there in your requested output and doesn't make much sense.
const convert = ({type, children, ordered, checked}) =>
type == 'root'
? {
type: 'list',
data: {style: ordered ? 'ordered' : 'unordered', items: convert (children [0])}
}
: type == 'list'
? children .map (convert)
: type == 'listItem'
? {
content: children [0] .children [0] .value,
checked: checked,
items: children [1] ? convert (children [1]) : []
}
: {error: `unknown type: '${type}'`}
const ast = {type: "root", children: [{type: "list", ordered: false, start: null, spread: false, children: [{type: "listItem", spread: false, checked: null, children: [{type: "paragraph", children: [{type: "text", value: "AAA"}]}, {type: "list", ordered: false, start: null, spread: false, children: [{type: "listItem", spread: false, checked: null, children: [{type: "paragraph", children: [{type: "text", value: "BBB"}]}]}]}]}, {type: "listItem", spread: false, checked: null, children: [{type: "paragraph", children: [{type: "text", value: "CCC"}]}, {type: "list", ordered: false, start: null, spread: false, children: [{type: "listItem", spread: false, checked: true, children: [{type: "paragraph", children: [{type: "text", value: "DDD"}]}, {type: "list", ordered: false, start: null, spread: false, children: [{type: "listItem", spread: false, checked: false, children: [{type: "paragraph", children: [{type: "text", value: "EEE"}]}]}, {type: "listItem", spread: false, checked: null, children: [{type: "paragraph", children: [{type: "text", value: "FFF"}]}]}]}]}]}]}]}]}
console .log (convert (ast))
.as-console-wrapper {max-height: 100% !important; top: 0}
I also changed the input to represent checked: true for DDD and checked: false for EEE to match your sample markup. The initial AST had null for both of those. I'm assuming that null is meant for "no checkbox here" and true and false would be used if there is one checked or unchecked. If that's not the case, then it might take a bit of fiddling.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | |
| Solution 2 | Calum Knott |
| Solution 3 |
