Given the following tree:
{
"name": "svg",
"type": "element",
"value": "",
"attributes": {
"xmlns": "http://www.w3.org/2000/svg",
"width": "1440",
"height": "1080"
},
"children": [{
"name": "g",
"type": "element",
"value": "",
"attributes": {},
"children": [{
"name": "g",
"type": "element",
"value": "",
"attributes": {},
"children": [{
"name": "g",
"type": "element",
"value": "",
"attributes": {
"fill": "#627580"
},
"children": [{
"name": "g",
"type": "element",
"value": "",
"attributes": {
"stroke-linejoin": "round"
},
"children": [{
"name": "path",
"type": "element",
"value": "",
"attributes": {
"d": "M150 0 L75 200 L225 200 Z"
},
"children": []
}, {
"name": "path",
"type": "element",
"value": "",
"attributes": {
"d": "M150 0 L75 200 L225 200 Z"
},
"children": []
}, {
"name": "path",
"type": "element",
"value": "",
"attributes": {
"d": "M150 0 L75 200 L225 200 Z"
},
"children": []
}]
}, {
"name": "g",
"type": "element",
"value": "",
"attributes": {
"stroke-linejoin": "round"
},
"children": [{
"name": "path",
"type": "element",
"value": "",
"attributes": {
"d": "M150 0 L75 200 L225 200 Z"
},
"children": []
}, {
"name": "path",
"type": "element",
"value": "",
"attributes": {
"d": "M150 0 L75 200 L225 200 Z"
},
"children": []
}, {
"name": "path",
"type": "element",
"value": "",
"attributes": {
"d": "M150 0 L75 200 L225 200 Z"
},
"children": []
}]
}]
}]
}]
}]
}
...which is a representation of this HTML:
<svg xmlns="http://www.w3.org/2000/svg" width="1440" height="1080">
<g>
<g>
<g fill="#627580">
<g stroke-linejoin="round">
<path d="M150 0 L75 200 L225 200 Z" />
<path d="M150 0 L75 200 L225 200 Z" />
<path d="M150 0 L75 200 L225 200 Z" />
</g>
<g stroke-linejoin="round">
<path d="M150 0 L75 200 L225 200 Z" />
<path d="M150 0 L75 200 L225 200 Z" />
<path d="M150 0 L75 200 L225 200 Z" />
</g>
</g>
</g>
</g>
</svg>
How do I transform this data structure recursively in JavaScript so all groups <g>
that have only one child <g>
are collapsed and their children are assigned to its parent?
Expected result would be this:
{
"name": "svg",
"type": "element",
"value": "",
"attributes": {
"xmlns": "http://www.w3.org/2000/svg",
"width": "1440",
"height": "1080"
},
"children": [{
"name": "g",
"type": "element",
"value": "",
"attributes": {},
"children": [{
"name": "g",
"type": "element",
"value": "",
"attributes": {
"stroke-linejoin": "round"
},
"children": [{
"name": "path",
"type": "element",
"value": "",
"attributes": {
"d": "M150 0 L75 200 L225 200 Z"
},
"children": []
}, {
"name": "path",
"type": "element",
"value": "",
"attributes": {
"d": "M150 0 L75 200 L225 200 Z"
},
"children": []
}, {
"name": "path",
"type": "element",
"value": "",
"attributes": {
"d": "M150 0 L75 200 L225 200 Z"
},
"children": []
}]
}, {
"name": "g",
"type": "element",
"value": "",
"attributes": {
"stroke-linejoin": "round"
},
"children": [{
"name": "path",
"type": "element",
"value": "",
"attributes": {
"d": "M150 0 L75 200 L225 200 Z"
},
"children": []
}, {
"name": "path",
"type": "element",
"value": "",
"attributes": {
"d": "M150 0 L75 200 L225 200 Z"
},
"children": []
}, {
"name": "path",
"type": "element",
"value": "",
"attributes": {
"d": "M150 0 L75 200 L225 200 Z"
},
"children": []
}]
}]
}]
}
...which is a representation of this HTML:
<svg xmlns="http://www.w3.org/2000/svg" width="1440" height="1080">
<g>
<g fill="#627580">
<g stroke-linejoin="round">
<path d="M150 0 L75 200 L225 200 Z" />
<path d="M150 0 L75 200 L225 200 Z" />
<path d="M150 0 L75 200 L225 200 Z" />
</g>
<g stroke-linejoin="round">
<path d="M150 0 L75 200 L225 200 Z" />
<path d="M150 0 L75 200 L225 200 Z" />
<path d="M150 0 L75 200 L225 200 Z" />
</g>
</g>
</g>
</svg>
Ideally there should not be a <g>
element remaining with a single child <g>
. In other words:
<g>
<g>
...
</g>
</g>
will be transformed into
<g>
...
</g>
This is my attempt:
const collapseGroups = node => {
if (node.children && node.children.length > 0) {
node.children.forEach(child => {
collapseGroups(child)
})
if (node.name == 'g' &&
node.children.length == 1 &&
node.children[0].name == 'g') {
node.children = node.children[0].children;
}
}
}
NB: My input is a SVG file, which is already automatically parsed into the above mentioned JS object.
You can use a recursive function on each node. If it detects it is of type "g", and only has one child, then the node absorbs the child, merging also the attributes property:
function simplify(node) {
return node.name === 'g' && node.children.length === 1
&& node.children[0].name === 'g'
? {
...simplify(node.children[0]),
attributes: { // Merge attributes of the two nodes that merge into one
...node.attributes,
...node.children[0].attributes
}
}
: {
...node,
children: node.children.map(simplify)
};
}
// Sample input:
let root = {
"name": "svg",
"type": "element",
"value": "",
"attributes": {
"xmlns": "http://www.w3.org/2000/svg",
"width": "1440",
"height": "1080"
},
"children": [{
"name": "g",
"type": "element",
"value": "",
"attributes": {},
"children": [{
"name": "g",
"type": "element",
"value": "",
"attributes": {},
"children": [{
"name": "g",
"type": "element",
"value": "",
"attributes": {
"fill": "#627580"
},
"children": [{
"name": "g",
"type": "element",
"value": "",
"attributes": {
"stroke-linejoin": "round"
},
"children": [{
"name": "path",
"type": "element",
"value": "",
"attributes": {
"d": "M150 0 L75 200 L225 200 Z"
},
"children": []
}, {
"name": "path",
"type": "element",
"value": "",
"attributes": {
"d": "M150 0 L75 200 L225 200 Z"
},
"children": []
}, {
"name": "path",
"type": "element",
"value": "",
"attributes": {
"d": "M150 0 L75 200 L225 200 Z"
},
"children": []
}]
}, {
"name": "g",
"type": "element",
"value": "",
"attributes": {
"stroke-linejoin": "round"
},
"children": [{
"name": "path",
"type": "element",
"value": "",
"attributes": {
"d": "M150 0 L75 200 L225 200 Z"
},
"children": []
}, {
"name": "path",
"type": "element",
"value": "",
"attributes": {
"d": "M150 0 L75 200 L225 200 Z"
},
"children": []
}, {
"name": "path",
"type": "element",
"value": "",
"attributes": {
"d": "M150 0 L75 200 L225 200 Z"
},
"children": []
}]
}]
}]
}]
}]
};
root = simplify(root);
console.log(root);