When working with a shared layer tree in a WebGIS application, you may need to remove empty directories so the tree can be used directly with a UI component like Element's Cascader. The goal is to produce a new array where only folders containing at least one layer are kept, and each layer node has its children set to null.
The original tree data, stored in store.state.layerManager.treeLayers, is an array of objects. Each object (either a folder or a layer) has a type property: 'dir' for folders and 'layer' for layers. They also have a children array: empty for layers, empty or populated for folders. Below is an example structure:
- 3D Model Data [π]
- City White Model [π]
- City DEM [π]
- City BIM [π]
- Spatio-temporal Base Data [π]
- City Electronic Map [π]
- Electronic Map 1 [π]
- Electronic Map 2 [π]
- Electronic Map 3 [π]
- City Remote Sensing Imagery [π]
- Remote Sensing Image 1 [π]
- Remote Sensing Image 2 [π]
- City Administrative Divisions [π]
- Division Map [π]
- 2023 Admin Division [π]
- 2020 Admin Division [π]
- Layer 1 [π]
- Layer 2 [π]
- City Park Data [π]
- City Metro Line Data [π]
Both folders and layers are objects. For instance:
treeLayers: [
{
name: '3D Model Data',
kind: 'dir',
items: [
{ name: 'City White Model', kind: 'layer', items: [] },
{ name: 'City DEM', kind: 'layer', items: [] }
]
},
// ...
{ name: 'City Metro Line Data', kind: 'dir', items: [] }
]
The challenge is to construct a new array, filteredTree, that omits any directory whose items array is empty (i.e., no layers anywhere in its subtree). Moreover, for leaf layers, we set their items to null so that Cascader treats them as selectable leaves.
We can achieve this with a recursive function that walks the original tree and only inculdes nodes that either have children (after filtering) or are themselves layers. Instead of manually tracking indices at each depth, we use a more flexible approach that builds the filtered tree by copying nodes and then pruning empty children recursively.
The key idea: start from the root array. For each node, if it is a layer or a directory that (after processing its children) still has at least one layer, we keep it. For directories, we rebuild their children array by applying the same filter. For layers, we mark the children as null.
Here's a clean implementation:
/**
* Recursively remove empty directories from the layer tree,
* and set children of leaf layers to null for Cascader compatibility.
*
* @param {Array} source - The original layer tree array
* @returns {Array} - Filtered tree without empty folders
*/
function pruneTree(source) {
return source.reduce((result, node) => {
const isLayer = node.kind === 'layer';
const isDir = node.kind === 'dir';
// Build a shallow copy of the node to avoid mutation
const copied = { ...node };
if (isLayer) {
// Leaf layer: set items to null for Cascader
copied.items = null;
result.push(copied);
} else if (isDir) {
// Directory: filter its children recursively
const filteredChildren = pruneTree(node.items);
if (filteredChildren.length > 0) {
copied.items = filteredChildren;
result.push(copied);
}
// If filteredChildren is empty, the directory is omitted.
}
return result;
}, []);
}
// Usage example:
const originalTree = this.$store.state.layerManager.treeLayers;
const filteredTree = pruneTree(originalTree);
This function replcaes the original stepβbyβstep approach with a compact, indexβfree recursion. It uses array.reduce to build the output array, and recursion handles nested directories. The result is a tree that contains only folders with at least one layer, and every layer node has its items set to null, which is exactly what Element's Cascader component expects for leaf nodes.
Finally, you can directly bind filteredTree to the options prop of the <el-cascader> component.