如何删除树中的单个 children?

How do I remove single children in a 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": []
          }]
        }]
      }]
    }]
  }]
}

...这是 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>

我如何在 JavaScript 中递归地转换此数据结构,以便所有只有一个 child <g> 的组 <g> 被折叠并且它们的 child ren 被分配到其 parent?

预期结果是这样的:

{
  "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": []
      }]
    }]
  }]
}

...这是 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>

理想情况下,不应有一个 <g> 元素与单个 child <g> 剩余。换句话说:

<g>
  <g>
  ...
  </g>
</g> 

将转化为

<g>
...
</g>

这是我的尝试:

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;
    }
  }
}

注意:我输入的是一个SVG文件,已经自动解析成上面提到的JS object.

您可以在每个节点上使用递归函数。如果它检测到它是“g”类型,并且只有一个子节点,则该节点吸收该子节点,并合并属性 属性:

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);

您可以 运行 结构的基本遍历,但是,如果您遇到具有单个 g 标签 child 的 g 标签,只需调用 merge 那个 child:

var d = {'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': []}]}]}]}]}]}
function merge(d){
    if (d.name != 'g' || d.children.length > 1 || d.children[0].name != 'g'){
       return {...d, 'children':d.children.map(merge)}
    }
    return merge(d.children[0])
}
console.log(merge(d))