树形数据处理工具 treelike-helper 用法说明

19 阅读4分钟

树形数据处理工具 treelike-helper 用法说明

基于之前的树形结构数据查找某个 key 的路径的函数为核心,我写了一个对树形(嵌套)数据处理的工具 treelike-helper

下面讲一下这个工具都有什么功能:

npm install treelike-helper

API

mapTreeData

类似 map,可以对树中每一个节点的数据进行操作

使用:

import { mapTreeData } from 'treelike-helper'

const treeData = [
  { id: '1', title: '1' },
  { id: '2', title: '2', children: [{ id: '2-1', title: '2-1' }] },
]
mapTreeData(treeData, item => {
  if (item.id === '2-1') {
    item.isLeaf = true
  }
  return {
    ...item,
    name: item.title,
    key: item.id,
  }
})

// [
//   { id: '1', title: '1', name: '1', key: '1' },
//   {
//     id: '2',
//     title: '2',
//     children: [{ id: '2-1', title: '2-1', isLeaf: true, name: '2-1', key: '2-1' }],
//     name: '2',
//     key: '2',
//   },
// ]

filterTreeData

类似 filter,需要注意的是,像下面示例这种情况,当父节点不满足时,子节点即使满足也会被筛选掉

import { filterTreeData } from 'treelike-helper'

const treeData = [
  { id: '1', title: '1', hasPermission: true },
  { id: '2', title: '2' },
  {
    id: '3',
    title: '3',
    hasPermission: true,
    children: [
      { id: '3-1', title: '3-1' },
      { id: '3-2', title: '3-2', hasPermission: true },
      { id: '3-3', title: '3-3', hasPermission: false },
    ],
  },
  {
    id: '4',
    title: '4',
    hasPermission: false,
    children: [
      { id: '4-1', title: '4-1' },
      { id: '4-2', title: '4-2', hasPermission: true },
    ],
  },
]
filterTreeData(treeData, (node) => node.hasPermission)

// [
//   { id: '1', title: '1', hasPermission: true },
//   {
//     id: '3',
//     title: '3',
//     hasPermission: true,
//     children: [{ id: '3-2', title: '3-2', hasPermission: true }],
//   },
// ]

mapFilterTreeData

map 和 filter 的结合,需要注意的是顺序不能变,第二个参数同 filterTreeData,第三个参数同 mapTreeData,且第三个参数时拿到的数据是已经筛选后的数据

import { mapFilterTreeData } from 'treelike-helper'

mapFilterTreeData(
  [
    { id: '1', title: '1', hasPermission: true },
    { id: '2', title: '2' },
    {
      id: '3',
      title: '3',
      hasPermission: true,
      children: [
        { id: '3-1', title: '3-1' },
        { id: '3-2', title: '3-2', hasPermission: true },
        { id: '3-3', title: '3-3', hasPermission: false },
      ],
    },
  ],
  item => item.hasPermission,
  item => ({ ...item, subTitle: 'already filter data' })
)

// [
//   {
//     id: '1',
//     title: '1',
//     hasPermission: true,
//     subTitle: 'already filter data',
//   },
//   {
//     id: '3',
//     title: '3',
//     hasPermission: true,
//     subTitle: 'already filter data',
//     children: [
//       {
//         id: '3-2',
//         title: '3-2',
//         hasPermission: true,
//         subTitle: 'already filter data',
//       },
//     ],
//   },
// ]

findKeyPath

查找 keyPath(Array)

findKeyPath([{ key: 1, children: [{key: 2}] }], 2)
// [0, 'children', 0]

findData

根据targetKey查找树中的某个节点

findData([{key: 1, children: [{ key: 2, children: [{ key: 3 }] }] }], 3)
// { key: 3 }

findParentData

根据targetKey查找父节点数据

const treeData = [
  {
    key: '1', children: [
      { key: '1-1' },
      {
        key: '1-2', children: [
          { key: '1-2-1' }
        ]
      }
    ]
  }
]
findParentData(treeData, '1-2-1')
// { key: '1-2', children: [{ key: '1-2-1' }] }

findSearchData

根据search查找所有相关的数据,但是并不是平铺的数据,而是会一直延伸到根节点,通常用于树形搜索

const treeData = [
  { key: '1', title: 'layer1' },
  {
    key: '2',
    title: '2',
    children: [
      { key: '2-1', title: '2-1' },
      {
        key: '2-2',
        title: '2-2',
        children: [{ key: '2-2-1', title: '2-2-1' }],
      },
      {
        key: '2-3',
        title: 'layer2-3',
      },
    ],
  },
];
findSearchData(treeData, 'lay')
// [
//   { key: '1', title: 'layer1' },
//   { key: '2', title: '2', children: [{ key: '2-3', title: 'layer2-3' }] },
// ]

addData

根据targetKey向该节点下添加数据

const treeData = [
  { key: '1' },
  {
    key: '2',
    children: [
      { key: '2-1' },
    ],
  },
];
const newTreeData1 = addData(treeData, '1', { key: '1-1', title: '1-1' })
// [
//   { key: '1', children: [{ key: '1-1', title: '1-1' }] },
//   { key: '2', children: [{ key: '2-1' }] },
// ]
const newTreeData2 = addData(treeData, '2-1', { key: '2-1-2' })
// [
//   { key: '1' },
//   { key: '2', children: [{ key: '2-1', children: [{ key: '2-1-2' }] }] },
// ]

deleteData

根据targetKey删除该节点(包括子节点)的数据,除此之外还有一个选项是deleteEmptyParent,当删除后父节点下的子节点为空,就删除子数组。

const treeData = [
  { key: 1 },
  { key: 2, children: [{ key: 22, children: [{ key: 33 }] }] },
];
deleteData(treeData, 2)
// [ { "key": 1 } ]
deleteData(treeData, 33)
// [
//   { key: 1 },
//   { key: 2, children: [{ key: 22, children: [] }] }
// ]
deleteData(treeData, 33, { deleteEmptyParent: true })
// [
//   { key: 1 }, 
//   { key: 2, children: [{ key: 22 }] }
// ];

updateData

根据targetKey修改节点数据

const treeData = [
  { key: 1 },
  { key: 2, children: [{ key: 22, children: [{ key: 33 }] }] },
];
updateData(treeData, 33, { key: 33, edit: true })
// [
//   { key: 1 }, 
//   { key: 2, children: [{ key: 22, children: [{ key: 33, edit: true }] }] }
// ];
updateData(treeData, 33, node => {
  return {
    ...node,
    edit: true,
  };
})
// [
//   { key: 1 }, 
//   { key: 2, children: [{ key: 22, children: [{ key: 33, edit: true }] }] }
// ];

updateThroughData

根据targetKey,修改该节点的所有途经父节点,默认不修改自身,添加includeSelf可以修改自身

const treeData = [
  { key: '1', children: [{ key: '1-1', children: [{ key: '1-1-1' }] }, { key: '1-2' }] },
]
updateThroughData(treeData, '1-1-1', node => {
  return { ...node, edited: true }
})
// [
//   {
//     key: '1',
//     children: [{ key: '1-1', children: [{ key: '1-1-1' }], edited: true }, { key: '1-2' }],
//     edited: true,
//   },
// ]

updateThroughData([{ key: 1, children: [{ key: 11 }] }], 11, node => {
  return { ...node, edited: true }
}, { includeSelf: true })
// [
//   { key: 1, 
//     children: [{ key: 11, edited: true }], 
//     edited: true 
//   }
// ]

getFieldValues

得到树形数据中所有给定field的值的Array,也可以使用在某些场景例如:想获得所有字段open为true的节点的key

const treeData = [
  { key: '1', children: [{ key: '11' }] },
  { key: '2', children: [{ key: '21', open: true }] },
  { key: '3', children: [{ key: '31' }] },
]
getFieldValues(treeData, 'key')
// ['1', '11', '2', '21', '3', '31']
getFieldValues(treeData, node => node.open ? node.key : null).filter(Boolean)
// ['21']

getFieldValueSet

和getFieldValue基本一直,只是返回的是去重的Set类型

calculateLeafCount

计算树形数据的叶子节点数量,只有没有子数据的节点才算叶子节点

const treeData = [
  {
    key: '1', title: 'parent 1', children: [
      { key: '1-1', title: 'leaf 1-1' },
      { key: '1-2', title: 'leaf 1-2' },
    ]
  },
  {
    key: '2', title: 'parent 2', children: [
      { key: '2-1', title: 'leaf 2-1' },
      { key: '2-2', title: 'leaf 2-2' },
    ]
  },
]
calculateLeafCount(treeData)
// 4

countNestedLayers

计算树形数据深度(最大值)

countNestedLayers([])
// 0
countNestedLayers([{ key: '1', title: '1' }])
// 1
countNestedLayers([{ key: '1', children: [{ key: '1-1' }] }])
// 2
countNestedLayers([
  { key: 1, children: [{ key: 11 }] },
  { key: 2, children: [{ key: 21, children: [{ key: 211 }] }] }
])
// 3

说明

  • 所有的这些方法都不会改变原数据
  • 如果数据的唯一值并不是"key",子数据不是"children",大多数函数都可以传递一个options,来指定子数组的keyName,或者是查询定位的keyName,例如:
findData([{id: 1, subs: [{ id: 2, subs: [{ id: 3 }] }] }], 3, {keyName: 'id', childrenKeyName: 'subs'})
//  { id: 3 }

具体情况可以看详细文档 treelike-helper