JS分类树的模糊匹配

前言

最近遇到个模糊匹配的小需求,需要对树形结构的内容进行模糊匹配,对树中的节点,只要是出现了相应文本,就把节点以上的整个树都展示出来
之前我写过一些针对id的匹配,和这个还不太一样,因为id的匹配是唯一的,只要进行一次递归(遍历),找到就能返回相应节点,也做过将树形数据可视化的功能,但是要根据规则记录树形结构,就稍微麻烦点。

分类树
4163539-13d810876a21a7d8.png
分类树.png

基本上就是这样一个分类树,数据结构如下:

const data = [{
    id: 1,
    txt: '测试1',
    children: [{
        id: 11,
        txt: '测试11',
        children: [{
            id: 111,
            txt: '测试111',
            children: [{
                id: 1111,
                txt: '这里就不是',
                children: []
            }]
        }]
    }, {
        id: 12,
        txt: '测试12',
        children: [{
            id: 121,
            txt: '测试121',
            children: [{
                id: 1211,
                txt: '测试1211',
                children: []
            }]
        }]
    }]
}, {
    id: 2,
    txt: '测试2',
    children: [{
        id: 21,
        txt: '测试21'
    }]
}]
要求

当我输入“测试”时,把所有包含“测试”节点都显示出来,也就是除了最后的“这里就不是”节点,其他的都要出来
当我输入“这里都不是”时,虽然他的父级节点都没有包含这个字符,但是作为他的父节点,所以也都要显示出来(作为树形结构)
当我输入“测试2”时,就只会显示根数组的第二个元素,因为只有这个分支中包含“测试2”的完整文本
总结一下,就是根据输入的文本,要显示出最深的包含此文本的节点的整个分支。

设计和代码

首先,根据需求,要对树进行遍历,给每一个节点一个字段,用来判断是否可以输出

// 给每个分支上有所匹配的设置isNeed
function setFlag (data, keyword) {
    return data.map(item => {
        item.isNeed = isNeedBranch(item, keyword)
        if(item.children && item.children.length) {
            setFlag(item.children, keyword)
        }
        return item
    })
}

这个判断的函数isNeedBranch也是一个递归,需要遍历整个树的每一个节点,只有当子节点中没有匹配到,并且自身也没有匹配到时,才会返回false

// 判断这条分支上有没有匹配到的
function isNeedBranch (item, keyword) {
    let flag1 = false,
        flag2 = false
    if(item.txt.indexOf(keyword) > -1) {
        flag1 = true
    } else if(item.children && item.children.length) {
        item.children.forEach(child => {
            if(isNeedBranch(child, keyword)) {
                flag2 = true
            }
        })
    }
    return flag1 || flag2
}

最后,对遍历过的树进行筛选,通过filter进行递归,把filter返回的值赋给chilren,完成筛选。

// 过滤掉分支上isNeed为false的元素
function treeFilter (data) {
    return data.filter((item, index) => {
        if(item && item.children && item.children.length) {
            item.children = treeFilter(item.children)
        }
        return item.isNeed
    })
}

拿到数据后,还要进行渲染,渲染出树形DOM,当然,有jsx或者模板的话,就不用这么麻烦
但是,要把拿到的数据进行一次deepcopy,或者每次点击都重新拿取,因为forEachmap方法都改变原数组

function getShowHTML(val, data) {
    if(!val) return ''
    let dataCopy = JSON.parse(JSON.stringify(data)) // 一个简单的deepCopy
    let filterTree = treeFilter(setFlag(dataCopy, val))
    return (filterTree && filterTree.length) ? getTreeHtml(filterTree) : ''
}

function getTreeHtml(tree) {
    let rootHTML = document.createElement('ul')
    rootHTML.style.id = 'root'
    let search = (nodeHTML, nodeJSON) => {
        nodeJSON.forEach(item => {
            // console.log('nodeHTML', nodeHTML)
            if(item.children && item.children.length) {
                let thisNode = document.createElement('li')
                let thisNodeUl = document.createElement('ul')
                thisNode.append(item.txt)
                thisNode.append(thisNodeUl)
                // console.log('thisNode.children', thisNode)
                search(thisNode.children[0], item.children)
                nodeHTML && nodeHTML.append(thisNode)
            } else {
                let li = document.createElement('li')
                li.append(item.txt)
                li.style.id = item.id
                nodeHTML && nodeHTML.append(li)
            }
        })
    }
    search(rootHTML, tree)
    return rootHTML
}
4163539-20ec1fcaba7dc948.png
image.png

猜你喜欢

转载自blog.csdn.net/weixin_34252090/article/details/87639197