首页 > 其他分享 >树结构操作通用方法

树结构操作通用方法

时间:2023-10-09 09:49:08浏览次数:32  
标签:node 通用 树结构 treeData param item key 操作 children

/**
 * 树操作通用方法,将一些常用方法提炼出来,方便使用。
 * @module 树操作工具
 */

import {cloneDeep} from 'lodash';
import {uniqueArray, arrayRemoveAll, arrayRemove} from './index';

/**
 * 将数据转换成tree所需格式
 * @param {object} data 要进行转换的object
 * @param {String} [keyField='id'] 指定data中某个字段转换为树所需的key
 * @param {String} [titleField='name'] 指定data中某个字段转换为树所需的name
 * @returns {{name: *, key: *}}
 */
export function generateTreeNode(data, keyField = 'id', titleField = 'name') {
    return {...data, title: data[titleField], key: data[keyField]};
}

/**
 * 将数据转换成tree所需格式
 * @param {Array} data 要进行转换的一些数据
 * @param {String} [keyField='id'] 指定data中某个字段转换为树所需的key
 * @param {String} [titleField='name'] 指定data中某个字段转换为树所需的name
 * @returns {Array}
 */
export function generateTreeNodes(data, keyField = 'id', titleField = 'name') {
    let arr = [];
    if (data && data.length) {
        arr = data.map(d => generateTreeNode(d, keyField, titleField));
    }
    return arr;
}

/**
 * 根据key 将node设置成叶子节点
 * @param {Array} treeData 树的树状结构数据
 * @param {String} key 节点的key值
 */
export function setLeaf(treeData, key) {
    const loopLeaf = (data) => {
        for (let item of data) {
            if (item.key === key) {
                item.isLeaf = true;
                break;
            }
            if (item.children && item.children.length) {
                loopLeaf(item.children);
            }
        }
    };
    loopLeaf(treeData);
}

/**
 * 给指定key的节点添加子节点
 * @param {Array} treeData 树的树状结构数据
 * @param {String} key 节点的key值
 * @param {Array} child 要添加的子节点
 */
export function appendChildrenByKey(treeData, key, child) {
    const loop = (data) => {
        for (let item of data) {
            if (key === item.key) {
                if (item.children) {
                    item.children = item.children.concat(child);
                } else {
                    item.children = child;
                }

                if (!item.children || !item.children.length) {
                    setLeaf(treeData, key);
                }
                break;
            }
            if (item.children && item.children.length) {
                loop(item.children);
            }
        }
    };
    loop(treeData);
}

/**
 * 检测某个节点是否有parent节点
 * @param {Array} rows 所有节点,扁平数据,非树状结构
 * @param {object} row 需要判断得节点
 * @returns {boolean}
 */
export function hasParent(rows, row) {
    let parentKey = row.parentKey;
    return rows.find(r => r.key === parentKey);
}

/**
 * 根据key,查询其所有后代节点,一般会用于删除
 * @param {Array} rows 具有key,parentKey关系的扁平数据结构
 * @param {object} key 要查询的节点 key
 * @returns {Array}
 */
export function getGenerationsByKey(rows, key) {
    // 这个函数会被多次调用,对rows做深拷贝,否则会产生副作用。
    rows = cloneDeep(rows);
    const parentNode = rows.find(item => item.key === key);
    if (!parentNode) return [];


    let nodes = [parentNode];
    let generationNodes = [cloneDeep(parentNode)];

    // 存放要处理的节点
    let toDo = nodes.map((v) => v);

    while (toDo.length) {
        // 处理一个,头部弹出一个。
        let node = toDo.shift();
        // 获取子节点。
        rows.forEach(row => {
            if (row.parentKey === node.key) {
                let child = cloneDeep(row);
                generationNodes.push(child);
                // child加入toDo,继续处理
                toDo.push(child);
            }
        });
    }
    return generationNodes;
}


/**
 * js构造树方法。会给节点添加parentKeys,parentNodes,parentTexts属性,方便后期数据提取
 * @param {Array} rows 具有key,parentKey关系的扁平数据结构,标题字段为text
 * @param {object} [parentNode=null] 开始节点
 * @returns {Array}
 */
export function convertToTree(rows, parentNode = null) {
    // 这个函数会被多次调用,对rows做深拷贝,否则会产生副作用。
    rows = cloneDeep(rows);
    parentNode = cloneDeep(parentNode);

    let nodes = [];
    if (parentNode) {
        nodes.push(parentNode);
    } else { // 获取所有的顶级节点
        nodes = rows.filter(r => !hasParent(rows, r));
    }

    // 存放要处理的节点
    let toDo = nodes.map((v) => v);

    while (toDo.length) {
        // 处理一个,头部弹出一个。
        let node = toDo.shift();
        // 获取子节点。
        rows.forEach(row => {
            if (row.parentKey === node.key) {
                let child = cloneDeep(row);
                let parentKeys = [node.key];
                if (node.parentKeys) {
                    parentKeys = node.parentKeys.concat(node.key);
                }
                child.parentKeys = parentKeys;

                let parentTexts = [node.text];
                if (node.parentTexts) {
                    parentTexts = node.parentTexts.concat(node.text);
                }
                child.parentTexts = parentTexts;

                const tempNode = cloneDeep(node);
                delete tempNode.children;
                delete tempNode.parentKeys;
                delete tempNode.parentNodes;
                delete tempNode.parentTexts;
                let parentNodes = [tempNode];
                if (node.parentNodes) {
                    parentNodes = node.parentNodes.concat(parentNodes);
                }
                child.parentNodes = parentNodes;

                if (node.children) {
                    node.children.push(child);
                } else {
                    node.children = [child];
                }
                // child加入toDo,继续处理
                toDo.push(child);
            }
        });
    }

    if (parentNode) {
        return nodes[0].children;
    }
    return nodes;
}

/**
 * 根据指定数据的键值对,查找node,比如根据path查找: getNodeByPropertyAndValue(treeData, 'path', '/user/list')
 * @param {Array} treeData 树状结构数据
 * @param {String} key key值,比如'path','text'等节点数据属性
 * @param {*} value 节点属性所对应的数据
 * @param {Function} [compare] 节点属性所对应的数据比较方式, 默认 === 比对
 * @returns {object} 返回根据 key value查找到的节点
 */
export function getNodeByPropertyAndValue(treeData, key, value, compare) {
    if (!treeData || !treeData.length) return null;
    if (!compare) compare = (a, b, item) => a === b;
    let node = null;
    const loop = (data) => {
        for (let item of data) {
            if (compare(item[key], value, item)) {
                node = {...item};
                break;
            }
            if (item.children && item.children.length) {
                loop(item.children);
            }
        }
    };
    loop(treeData);
    return node;
}

/**
 * 根据key查找节点
 * @param {Array} treeData 树状结构数据
 * @param {String} key
 * @returns {object} 根据key查找到的节点
 */
export function getNodeByKey(treeData, key) {
    return getNodeByPropertyAndValue(treeData, 'key', key);
}

/**
 * 根据key查找后代元素的key
 * @param {Array} treeData 树状结构数据
 * @param {String} key
 * @returns {*[]} 根据key查找到的所有后代节点key
 */
export function getGenerationKeys(treeData, key) {
    const node = getNodeByKey(treeData, key);
    const keys = [];
    const loop = (node) => {
        const {key, children} = node;
        if (children?.length) {
            children.forEach(loop);
        } else {
            keys.push(key);
        }
    };
    loop(node);

    return keys.filter(item => item !== key);
}

/**
 * 根据key查找所有后代元素
 * @param {Array} treeData 树状结构数据
 * @param {String} key
 * @returns {Array} 根据key查找到的所有后代节点
 */
export function getGenerationalNodesByKey(treeData, key) {
    const node = getNodeByKey(treeData, key);
    if (!node.children || !node.children.length) {
        return [];
    }
    const allNodes = [];
    const loop = (data) => {
        data.forEach(d => {
            allNodes.push(d);
            if (d.children && d.children) {
                loop(d.children);
            }
        });
    };
    loop(node.children);
    return allNodes;
}

/**
 * 获取选中节点的keys,点击父节点时,其下所有后代元素将被全被选中,或者全不选中,选中子节点时,其所有祖先节点将被选中
 * @param treeData 树状结构数据
 * @param {Array} checkedKeys 点击过之后,树选中的keys
 * @param {boolean} checked 当前点击时 checked (true)还是 unchecked(false)
 * @param {String} checkNodeKey 当前点击节点的key
 * @returns {Array} 选中的keys
 */
export function getCheckedKeys(treeData, checkedKeys, checked, checkNodeKey) {
    // TODO 区分半选和全选
    let allKeys = [...checkedKeys];
    const generationalNodes = getGenerationalNodesByKey(treeData, checkNodeKey);
    const generationalKeys = generationalNodes.map(n => n.key);

    if (checked) {
        // 选中所有后代节点
        allKeys = allKeys.concat(generationalKeys);

        // 选中有祖先节点
        const node = getNodeByKey(treeData, checkNodeKey);
        if (node.parentKeys) {
            allKeys = allKeys.concat(node.parentKeys);
        }
    } else {
        // 取消选中所有后代节点
        allKeys = arrayRemoveAll(allKeys, generationalKeys.concat(checkNodeKey));

        // 判断其父节点是否还有子节点选中了,如果没有,父节点也不选中
        const node = getNodeByKey(treeData, checkNodeKey);
        if (node.parentKeys) {
            const pks = [...node.parentKeys];
            pks.reverse();
            pks.forEach(key => {
                const pNode = getNodeByKey(treeData, key);
                if (pNode.children && pNode.children.length) {
                    let hasCheckedChild = false;
                    for (let pCNode of pNode.children) {
                        if (allKeys.indexOf(pCNode.key) > -1) {
                            hasCheckedChild = true;
                            break;
                        }
                    }
                    if (!hasCheckedChild) {
                        allKeys = arrayRemove(allKeys, key);
                    }
                }
            });
        }
    }
    return uniqueArray(allKeys);
}

/**
 * 根据key删除节点
 * @param {Array} treeData 树的树状结构数据
 * @param {String} key 要删除节点的key值
 */
export function removeNodeByKey(treeData, key) {
    if (!treeData || !treeData.length) return null;
    const loop = (data) => {
        for (let i = 0; i < data.length; i++) {
            const item = data[i];
            if (item.key === key) {
                data.splice(i, 1);
                break;
            } else if (item.children && item.children.length) {
                loop(item.children);
            }
        }
    };
    loop(treeData);
}

/**
 * 给指定key的node节点增加一个新的子节点
 * @param {Array} treeData 树的树状结构数据
 * @param {String} key 要操作的节点的key值
 * @param {object} newNode 需要加入的子节点
 */
export function addNodeChildByKey(treeData, key, newNode) {
    if (!treeData || !treeData.length) return null;
    newNode.isLeaf = true;
    const loop = (data) => {
        for (let item of data) {
            if (item.key === key) {
                if (item.children) {
                    item.children.push({...newNode});
                } else {
                    item.children = [{...newNode}];
                }
                break;
            }
            if (item.children && item.children.length) {
                loop(item.children);
            }
        }
    };
    loop(treeData);
}

/**
 * 更新某个节点
 * @param {Array} treeData 树的树状结构数据
 * @param {object} newNode 需要跟新的节点新数据,会根据key对原数据进行比对
 */
export function updateNode(treeData, newNode) {
    if (!treeData || !treeData.length) return null;
    const loop = (data) => {
        for (let item of data) {
            if (item.key === newNode.key) {
                Object.keys(item).forEach(key => {
                    item[key] = newNode[key];
                });
                break;
            }
            if (item.children && item.children.length) {
                loop(item.children);
            }
        }
    };
    loop(treeData);
}

/**
 * 根据某个节点,获取其最顶级节点
 * @param {Array} treeData 树状结构数据
 * @param {object} node 节点数据
 * @returns {object} 最顶层节点
 */
export function getTopNodeByNode(treeData, node) {
    if (!treeData || !treeData.length || !node) return null;

    if (node && !node.parentKey) return node;
    let parentNode = null;
    const loop = (data) => { // 查找node的父节点
        for (let item of data) {
            if (item.key === node.parentKey) {
                parentNode = {...item};
                break;
            }
            if (item.children && item.children.length) {
                loop(item.children);
            }
        }
    };
    loop(treeData);
    return getTopNodeByNode(treeData, parentNode); // 继续查找parentNode的父节点
}

/**
 * 渲染树,cb(node[, children nodes])
 * @param {Array} treeData 树的树状结构数据
 * @param {function} cb 回调函数:cb(node[, children nodes])
 */
export function renderNode(treeData, cb) {
    const loop = data => data.map((item) => {
        if (item.children) {
            return cb(item, loop(item.children)); // item children Item
        }

        return cb(item); // 叶子节点
    });
    return loop(treeData);
}

/**
 * 查找给定节点,及其后代节点property属性,第一个不为空的值
 * @param {Array} treeData 树的树状结构数据
 * @param {object} node 节点数据
 * @param {String} property 属性,比如 key, path等
 * @returns {*}
 */
export function getFirstValue(treeData, node, property) {
    if (node[property]) return node[property];
    let firstValue = null;
    const loop = data => {
        for (let item of data) {
            if (item[property]) {
                firstValue = item[property];
                break;
            }
            if (item.children && item.children.length) {
                loop(item.children);
            }
        }
    };
    if (node.children && node.children.length) {
        loop(node.children);
    }
    return firstValue;
}

 

/**  * 树操作通用方法,将一些常用方法提炼出来,方便使用。  * @module树操作工具  */
import {cloneDeep} from 'lodash'; import {uniqueArray, arrayRemoveAll, arrayRemove} from './index';
/**  * 将数据转换成tree所需格式  * @param{object}data 要进行转换的object  * @param{String}[keyField='id'] 指定data中某个字段转换为树所需的key  * @param{String}[titleField='name'] 指定data中某个字段转换为树所需的name  * @returns{{name: *, key: *}}  */ export function generateTreeNode(data, keyField = 'id', titleField = 'name') {     return {...data, title: data[titleField], key: data[keyField]}; }
/**  * 将数据转换成tree所需格式  * @param{Array}data 要进行转换的一些数据  * @param{String}[keyField='id'] 指定data中某个字段转换为树所需的key  * @param{String}[titleField='name'] 指定data中某个字段转换为树所需的name  * @returns{Array}  */ export function generateTreeNodes(data, keyField = 'id', titleField = 'name') {     let arr = [];     if (data && data.length) {         arr = data.map(d => generateTreeNode(d, keyField, titleField));     }     return arr; }
/**  * 根据key 将node设置成叶子节点  * @param{Array}treeData 树的树状结构数据  * @param{String}key 节点的key值  */ export function setLeaf(treeData, key) {     const loopLeaf = (data) => {         for (let item of data) {             if (item.key === key) {                 item.isLeaf = true;                 break;             }             if (item.children && item.children.length) {                 loopLeaf(item.children);             }         }     };     loopLeaf(treeData); }
/**  * 给指定key的节点添加子节点  * @param{Array}treeData 树的树状结构数据  * @param{String}key 节点的key值  * @param{Array}child 要添加的子节点  */ export function appendChildrenByKey(treeData, key, child) {     const loop = (data) => {         for (let item of data) {             if (key === item.key) {                 if (item.children) {                     item.children = item.children.concat(child);                 } else {                     item.children = child;                 }
                if (!item.children || !item.children.length) {                     setLeaf(treeData, key);                 }                 break;             }             if (item.children && item.children.length) {                 loop(item.children);             }         }     };     loop(treeData); }
/**  * 检测某个节点是否有parent节点  * @param{Array}rows 所有节点,扁平数据,非树状结构  * @param{object}row 需要判断得节点  * @returns{boolean}  */ export function hasParent(rows, row) {     let parentKey = row.parentKey;     return rows.find(r => r.key === parentKey); }
/**  * 根据key,查询其所有后代节点,一般会用于删除  * @param{Array}rows 具有key,parentKey关系的扁平数据结构  * @param{object}key 要查询的节点 key  * @returns{Array}  */ export function getGenerationsByKey(rows, key) {     // 这个函数会被多次调用,对rows做深拷贝,否则会产生副作用。     rows = cloneDeep(rows);     const parentNode = rows.find(item => item.key === key);     if (!parentNode) return [];

    let nodes = [parentNode];     let generationNodes = [cloneDeep(parentNode)];
    // 存放要处理的节点     let toDo = nodes.map((v) => v);
    while (toDo.length) {         // 处理一个,头部弹出一个。         let node = toDo.shift();         // 获取子节点。         rows.forEach(row => {             if (row.parentKey === node.key) {                 let child = cloneDeep(row);                 generationNodes.push(child);                 // child加入toDo,继续处理                 toDo.push(child);             }         });     }     return generationNodes; }

/**  * js构造树方法。会给节点添加parentKeys,parentNodes,parentTexts属性,方便后期数据提取  * @param{Array}rows 具有key,parentKey关系的扁平数据结构,标题字段为text  * @param{object}[parentNode=null] 开始节点  * @returns{Array}  */ export function convertToTree(rows, parentNode = null) {     // 这个函数会被多次调用,对rows做深拷贝,否则会产生副作用。     rows = cloneDeep(rows);     parentNode = cloneDeep(parentNode);
    let nodes = [];     if (parentNode) {         nodes.push(parentNode);     } else { // 获取所有的顶级节点         nodes = rows.filter(r => !hasParent(rows, r));     }
    // 存放要处理的节点     let toDo = nodes.map((v) => v);
    while (toDo.length) {         // 处理一个,头部弹出一个。         let node = toDo.shift();         // 获取子节点。         rows.forEach(row => {             if (row.parentKey === node.key) {                 let child = cloneDeep(row);                 let parentKeys = [node.key];                 if (node.parentKeys) {                     parentKeys = node.parentKeys.concat(node.key);                 }                 child.parentKeys = parentKeys;
                let parentTexts = [node.text];                 if (node.parentTexts) {                     parentTexts = node.parentTexts.concat(node.text);                 }                 child.parentTexts = parentTexts;
                const tempNode = cloneDeep(node);                 delete tempNode.children;                 delete tempNode.parentKeys;                 delete tempNode.parentNodes;                 delete tempNode.parentTexts;                 let parentNodes = [tempNode];                 if (node.parentNodes) {                     parentNodes = node.parentNodes.concat(parentNodes);                 }                 child.parentNodes = parentNodes;
                if (node.children) {                     node.children.push(child);                 } else {                     node.children = [child];                 }                 // child加入toDo,继续处理                 toDo.push(child);             }         });     }
    if (parentNode) {         return nodes[0].children;     }     return nodes; }
/**  * 根据指定数据的键值对,查找node,比如根据path查找: getNodeByPropertyAndValue(treeData, 'path', '/user/list')  * @param{Array}treeData 树状结构数据  * @param{String}key key值,比如'path','text'等节点数据属性  * @param{*}value 节点属性所对应的数据  * @param{Function}[compare] 节点属性所对应的数据比较方式, 默认 === 比对  * @returns{object} 返回根据 key value查找到的节点  */ export function getNodeByPropertyAndValue(treeData, key, value, compare) {     if (!treeData || !treeData.length) return null;     if (!compare) compare = (a, b, item) => a === b;     let node = null;     const loop = (data) => {         for (let item of data) {             if (compare(item[key], value, item)) {                 node = {...item};                 break;             }             if (item.children && item.children.length) {                 loop(item.children);             }         }     };     loop(treeData);     return node; }
/**  * 根据key查找节点  * @param{Array}treeData 树状结构数据  * @param{String}key  * @returns{object} 根据key查找到的节点  */ export function getNodeByKey(treeData, key) {     return getNodeByPropertyAndValue(treeData, 'key', key); }
/**  * 根据key查找后代元素的key  * @param{Array}treeData 树状结构数据  * @param{String}key  * @returns{*[]} 根据key查找到的所有后代节点key  */ export function getGenerationKeys(treeData, key) {     const node = getNodeByKey(treeData, key);     const keys = [];     const loop = (node) => {         const {key, children} = node;         if (children?.length) {             children.forEach(loop);         } else {             keys.push(key);         }     };     loop(node);
    return keys.filter(item => item !== key); }
/**  * 根据key查找所有后代元素  * @param{Array}treeData 树状结构数据  * @param{String}key  * @returns{Array} 根据key查找到的所有后代节点  */ export function getGenerationalNodesByKey(treeData, key) {     const node = getNodeByKey(treeData, key);     if (!node.children || !node.children.length) {         return [];     }     const allNodes = [];     const loop = (data) => {         data.forEach(d => {             allNodes.push(d);             if (d.children && d.children) {                 loop(d.children);             }         });     };     loop(node.children);     return allNodes; }
/**  * 获取选中节点的keys,点击父节点时,其下所有后代元素将被全被选中,或者全不选中,选中子节点时,其所有祖先节点将被选中  * @paramtreeData 树状结构数据  * @param{Array}checkedKeys 点击过之后,树选中的keys  * @param{boolean}checked 当前点击时 checked (true)还是 unchecked(false)  * @param{String}checkNodeKey 当前点击节点的key  * @returns{Array} 选中的keys  */ export function getCheckedKeys(treeData, checkedKeys, checked, checkNodeKey) {     // TODO 区分半选和全选     let allKeys = [...checkedKeys];     const generationalNodes = getGenerationalNodesByKey(treeData, checkNodeKey);     const generationalKeys = generationalNodes.map(n => n.key);
    if (checked) {         // 选中所有后代节点         allKeys = allKeys.concat(generationalKeys);
        // 选中有祖先节点         const node = getNodeByKey(treeData, checkNodeKey);         if (node.parentKeys) {             allKeys = allKeys.concat(node.parentKeys);         }     } else {         // 取消选中所有后代节点         allKeys = arrayRemoveAll(allKeys, generationalKeys.concat(checkNodeKey));
        // 判断其父节点是否还有子节点选中了,如果没有,父节点也不选中         const node = getNodeByKey(treeData, checkNodeKey);         if (node.parentKeys) {             const pks = [...node.parentKeys];             pks.reverse();             pks.forEach(key => {                 const pNode = getNodeByKey(treeData, key);                 if (pNode.children && pNode.children.length) {                     let hasCheckedChild = false;                     for (let pCNode of pNode.children) {                         if (allKeys.indexOf(pCNode.key) > -1) {                             hasCheckedChild = true;                             break;                         }                     }                     if (!hasCheckedChild) {                         allKeys = arrayRemove(allKeys, key);                     }                 }             });         }     }     return uniqueArray(allKeys); }
/**  * 根据key删除节点  * @param{Array}treeData 树的树状结构数据  * @param{String}key 要删除节点的key值  */ export function removeNodeByKey(treeData, key) {     if (!treeData || !treeData.length) return null;     const loop = (data) => {         for (let i = 0; i < data.length; i++) {             const item = data[i];             if (item.key === key) {                 data.splice(i, 1);                 break;             } else if (item.children && item.children.length) {                 loop(item.children);             }         }     };     loop(treeData); }
/**  * 给指定key的node节点增加一个新的子节点  * @param{Array}treeData 树的树状结构数据  * @param{String}key 要操作的节点的key值  * @param{object}newNode 需要加入的子节点  */ export function addNodeChildByKey(treeData, key, newNode) {     if (!treeData || !treeData.length) return null;     newNode.isLeaf = true;     const loop = (data) => {         for (let item of data) {             if (item.key === key) {                 if (item.children) {                     item.children.push({...newNode});                 } else {                     item.children = [{...newNode}];                 }                 break;             }             if (item.children && item.children.length) {                 loop(item.children);             }         }     };     loop(treeData); }
/**  * 更新某个节点  * @param{Array}treeData 树的树状结构数据  * @param{object}newNode 需要跟新的节点新数据,会根据key对原数据进行比对  */ export function updateNode(treeData, newNode) {     if (!treeData || !treeData.length) return null;     const loop = (data) => {         for (let item of data) {             if (item.key === newNode.key) {                 Object.keys(item).forEach(key => {                     item[key] = newNode[key];                 });                 break;             }             if (item.children && item.children.length) {                 loop(item.children);             }         }     };     loop(treeData); }
/**  * 根据某个节点,获取其最顶级节点  * @param{Array}treeData 树状结构数据  * @param{object}node 节点数据  * @returns{object} 最顶层节点  */ export function getTopNodeByNode(treeData, node) {     if (!treeData || !treeData.length || !node) return null;
    if (node && !node.parentKey) return node;     let parentNode = null;     const loop = (data) => { // 查找node的父节点         for (let item of data) {             if (item.key === node.parentKey) {                 parentNode = {...item};                 break;             }             if (item.children && item.children.length) {                 loop(item.children);             }         }     };     loop(treeData);     return getTopNodeByNode(treeData, parentNode); // 继续查找parentNode的父节点 }
/**  * 渲染树,cb(node[, children nodes])  * @param{Array}treeData 树的树状结构数据  * @param{function}cb 回调函数:cb(node[, children nodes])  */ export function renderNode(treeData, cb) {     const loop = data => data.map((item) => {         if (item.children) {             return cb(item, loop(item.children)); // item children Item         }
        return cb(item); // 叶子节点     });     return loop(treeData); }
/**  * 查找给定节点,及其后代节点property属性,第一个不为空的值  * @param{Array}treeData 树的树状结构数据  * @param{object}node 节点数据  * @param{String}property 属性,比如 key, path等  * @returns{*}  */ export function getFirstValue(treeData, node, property) {     if (node[property]) return node[property];     let firstValue = null;     const loop = data => {         for (let item of data) {             if (item[property]) {                 firstValue = item[property];                 break;             }             if (item.children && item.children.length) {                 loop(item.children);             }         }     };     if (node.children && node.children.length) {         loop(node.children);     }     return firstValue; }

标签:node,通用,树结构,treeData,param,item,key,操作,children
From: https://www.cnblogs.com/hellofangfang/p/17750752.html

相关文章

  • linux如何查看操作系统版本信息
    linux查看版本信息,命令更全面。一、linux下如何查看已安装的centos版本信息:1.Linux查看当前操作系统版本信息 cat/proc/versionLinuxversion2.6.32-696.el6.x86_64([email protected])(gccversion4.4.720120313(RedHat4.4.7-18)(GCC))#1SMPTueMa......
  • 安卓开发图片动态操作,利用SeekBar控制属性示例
    屏幕大小适配演示,综合练习。功能为,用一个滑块来控制图片的旋转,用一个滑块来控制图片大小,核心语法思路,控制图片的大小,mImageView.setLayoutParams(newLinearLayout.LayoutParams(newWidth,newHeight));但是这儿二个属性要提前配置,并且图片大不能超出屏幕,所以先计算屏幕大小,pr......
  • 操作系统(7)---进程通信、进程互斥
    一、进程通信进程通信是进程之间的信息交换。主要有三种方式:管道通信e.gps-aux|grepexp(左边只能写,右边只能读,半双工管道,管道在内核)管道的实质是一个用于连接读写进程的一个共享文件,固定大小的缓冲区。数据一旦被读出,则从管道中丢弃。没写满不读,没读空不写。......
  • 记一次某通用供应链管理系统文件上传漏洞挖掘
    引言本文记录了一次对某通用供应链管理系统文件上传漏洞的挖掘,该通用产品使用了tomcat,但没有配置好tomcat管理后台的认证信息,导致了tomcat后台文件上传漏洞的发生。经测试,大部分使用该产品的资产均存在tomcat后台文件上传Nday的影响。漏洞挖掘路径FUZZ发现tomcat后台接口manag......
  • 记一次某通用工控设备管理平台 SQL注入漏洞挖掘(CNVD-2023-59080)
    记录一次本人CNVD漏洞挖掘的过程,此漏洞已被分配编号:CNVD-2023-59080引言本文记录了一次对某通用工控设备管理平台基于布尔盲注的SQL注入漏洞的挖掘,存在漏洞的接口是日志查询功能,其中的keyword参数存在SQL注入。漏洞挖掘管理平台需要用户名密码授权,且需要验证码,尝试了几个弱......
  • 记一次某大型集团内部通用OA系统 SQL注入漏洞挖掘
    引言本文记录了一次对某大型集团内部通用OA系统基于布尔盲注的SQL注入漏洞的挖掘,因内容敏感,大部分区域均马赛克处理。漏洞挖掘在常规的挖掘中没有什么突破口,但是在找回密码处发现一个功能接口,允许用户通过邮箱找回密码,接收的参数为工号,设有验证码,但在简单测试之后发现验证码是......
  • 记一次某通用供应链管理系统前台 SQL注入漏洞挖掘
    引言本文记录了一次对某通用供应链管理系统前台基于时间盲注的SQL注入漏洞的挖掘,存在漏洞的接口是登录功能,其中的用户名参数存在SQL注入。漏洞挖掘前台登录接口抓包,尝试构造单引号,发现系统做了防注入防护,返回非法字符注入失败页面。这种情况尝试构造时间盲注,看看页面是否会......
  • 用程序小小11孩岁女A片㓜或操作系统使用的逻辑地址式与具体的内存管理单元(MMU)的实现有
    在ARM架构中,块地址是物理地址的一部分,用于标识cache中存储数据的物理位置。索引(index)是用于确定cache行对应的物理地址的部分,而标记(tag)则用于标识cache行中所存储数据的虚拟地址。当CPU访问虚拟地址时,硬件会根据虚拟地址与索引掩码进行按位与(AND)操作,得到索引,然后使用索引和标记在c......
  • 8、操作系统
    1、操作系统1.1、基本概念操作系统呢,简称OS,是管理和控制计算机硬件和软件资源的计算机程序是用户和计算机的接口同时也是计算机的硬件和其他软件的接口总结:操作系统是一个程序,是一个庞大的程序,集成了很多功能作用:人机接口:我们人是不能直接跟计算机进行沟通的,这里的操作......
  • 分布式操作系统市场规模有万亿吗?你怎么看?
    昨天参加一个饭局,席间和一位做ICT基础设施投资的朋友聊起我们团队研发的LAXCUS分布式操作系统,请他分析一下分布式操作系统和连带的产业市场规模价值,我给出的分析依据是:1.LAXCUS面向TOB市场,有广泛应用前景,包括AI、大数据、HPC的支持2.LAXCUS属于多机操作系统,和Windows、Linux单......