@@ -112,105 +112,6 @@ export function arrayRemove<V>(array: V[], expect: (val: V, idx: number) => bool
112112 return array ;
113113}
114114
115- /**
116- * 自定义深度优先遍历函数(支持continue和break操作)
117- * @param {ArrayLike<V> } tree 树形数据
118- * @param {Function } iterator 迭代函数, 返回值为true时continue, 返回值为false时break
119- * @param {string } children 定制子元素的key
120- * @param {boolean } isReverse 是否反向遍历
121- * @returns {* }
122- */
123- export function forEachDeep < V > (
124- tree : ArrayLike < V > ,
125- iterator : ( val : V , i : number , arr : ArrayLike < V > , parent : V | null , level : number ) => boolean | void ,
126- children : string = 'children' ,
127- isReverse = false
128- ) {
129- let level = 0 ,
130- isBreak = false ;
131- const walk = ( arr : ArrayLike < V > , parent : V | null ) => {
132- if ( isReverse ) {
133- for ( let i = arr . length - 1 ; i >= 0 ; i -- ) {
134- if ( isBreak ) {
135- break ;
136- }
137- const re = iterator ( arr [ i ] , i , tree , parent , level ) ;
138- if ( re === false ) {
139- isBreak = true ;
140- break ;
141- } else if ( re === true ) {
142- continue ;
143- }
144- // @ts -ignore
145- if ( Array . isArray ( arr [ i ] [ children ] ) ) {
146- ++ level ;
147- // @ts -ignore
148- walk ( arr [ i ] [ children ] , arr [ i ] ) ;
149- }
150- }
151- } else {
152- for ( let i = 0 ; i < arr . length ; i ++ ) {
153- if ( isBreak ) {
154- break ;
155- }
156- const re = iterator ( arr [ i ] , i , tree , parent , level ) ;
157- if ( re === false ) {
158- isBreak = true ;
159- break ;
160- } else if ( re === true ) {
161- continue ;
162- }
163- // @ts -ignore
164- if ( Array . isArray ( arr [ i ] [ children ] ) ) {
165- ++ level ;
166- // @ts -ignore
167- walk ( arr [ i ] [ children ] , arr [ i ] ) ;
168- }
169- }
170- }
171- } ;
172- walk ( tree , null ) ;
173- }
174- export type IdLike = number | string ;
175- export interface ITreeConf {
176- id : string | number ;
177- children : string ;
178- }
179- /**
180- * 在树中找到 id 为某个值的节点,并返回上游的所有父级节点
181- *
182- * @param {ArrayLike<T> } tree - 树形数据
183- * @param {IdLike } nodeId - 元素ID
184- * @param {ITreeConf } config - 迭代配置项
185- * @returns {[IdLike[], ITreeItem<V>[]] } - 由parentId...childId, parentObject-childObject组成的二维数组
186- */
187- export function searchTreeById < V > ( tree : ArrayLike < V > , nodeId : IdLike , config ?: ITreeConf ) : [ IdLike [ ] , ArrayLike < V > [ ] ] {
188- const { children = 'children' , id = 'id' } = config || { } ;
189- const toFlatArray = ( tree , parentId ?: IdLike , parent ?: any ) => {
190- return tree . reduce ( ( t , _ ) => {
191- const child = _ [ children ] ;
192- return [
193- ...t ,
194- parentId ? { ..._ , parentId, parent } : _ ,
195- ...( child && child . length ? toFlatArray ( child , _ [ id ] , _ ) : [ ] )
196- ] ;
197- } , [ ] ) ;
198- } ;
199- const getIds = ( flatArray ) : [ IdLike [ ] , ArrayLike < V > [ ] ] => {
200- let child = flatArray . find ( _ => _ [ id ] === nodeId ) ;
201- const { parent, parentId, ...other } = child ;
202- let ids = [ nodeId ] ,
203- nodes = [ other ] ;
204- while ( child && child . parentId ) {
205- ids = [ child . parentId , ...ids ] ;
206- nodes = [ child . parent , ...nodes ] ;
207- child = flatArray . find ( _ => _ [ id ] === child . parentId ) ; // eslint-disable-line
208- }
209- return [ ids , nodes ] ;
210- } ;
211- return getIds ( toFlatArray ( tree ) ) ;
212- }
213-
214115/**
215116 * 异步ForEach函数
216117 * @param {array } array
@@ -239,104 +140,3 @@ async function asyncForEach(array: any[], callback: Function) {
239140 await callback ( array [ index ] , index , array ) ;
240141 }
241142}
242-
243- /**
244- * 使用迭代函数转换数组
245- * @param {T } array
246- * @param {Function } callback 迭代函数
247- * @return {Array }
248- */
249- function flatMap < T , U > ( array : T [ ] , callback : ( value : T , index : number , array : T [ ] ) => U [ ] ) : U [ ] {
250- const result : U [ ] = [ ] ;
251-
252- array . forEach ( ( value , index ) => {
253- result . push ( ...callback ( value , index , array ) ) ;
254- } ) ;
255-
256- return result ;
257- }
258-
259- export type WithChildren < T > = T & { children ?: WithChildren < T > [ ] } ;
260-
261- /**
262- * 根据 idProp 与 parentIdProp 从对象数组中构建对应的树
263- * 当 A[parentIdProp] === B[idProp] 时,对象A会被移动到对象B的children。
264- * 当一个对象的 parentIdProp 不与其他对象的 idProp 字段相等时,该对象被作为树的顶层节点
265- * @param {string } idProp 元素ID
266- * @param {string } parentIdProp 父元素ID
267- * @param {object[] } items 一维数组
268- * @returns {WithChildren<T>[] } 树
269- * @example
270- * const array = [
271- * { id: 'node-1', parent: 'root' },
272- * { id: 'node-2', parent: 'root' },
273- * { id: 'node-3', parent: 'node-2' },
274- * { id: 'node-4', parent: 'node-2' },
275- * { id: 'node-5', parent: 'node-4' },
276- * ]
277- * const tree = buildTree('id', 'parent', array)
278- * expect(tree).toEqual([
279- * { id: 'node-1', parent: 'root' },
280- * {
281- * id: 'node-2',
282- * parent: 'root',
283- * children: [
284- * { id: 'node-3', parent: 'node-2' },
285- * {
286- * id: 'node-4',
287- * parent: 'node-2',
288- * children: [{ id: 'node-5', parent: 'node-4' }],
289- * },
290- * ],
291- * },
292- * ])
293- */
294- export function buildTree < ID extends string , PID extends string , T extends { [ key in ID | PID ] : string } > (
295- idProp : ID ,
296- parentIdProp : PID ,
297- items : T [ ]
298- ) : WithChildren < T > [ ] {
299- type Wrapper = { id : string ; children : Wrapper [ ] ; item : T ; parent : Wrapper } ;
300-
301- const wrapperMap = new Map < string , Wrapper > ( ) ;
302- const ensure = ( id : string ) => {
303- if ( wrapperMap . has ( id ) ) {
304- return wrapperMap . get ( id ) ;
305- }
306- //@ts -ignore
307- const wrapper : Wrapper = { id, parent : null , item : null , children : [ ] } ;
308- wrapperMap . set ( id , wrapper ) ;
309- return wrapper ;
310- } ;
311- for ( const item of items ) {
312- const parentWrapper = ensure ( item [ parentIdProp ] ) ;
313- const itemWrapper = ensure ( item [ idProp ] ) ;
314- //@ts -ignore
315- itemWrapper . parent = parentWrapper ;
316- //@ts -ignore
317- parentWrapper . children . push ( itemWrapper ) ;
318- //@ts -ignore
319- itemWrapper . item = item ;
320- }
321- const topLevelWrappers = flatMap (
322- Array . from ( wrapperMap . values ( ) ) . filter ( wrapper => wrapper . parent === null ) ,
323- wrapper => wrapper . children
324- ) ;
325-
326- return unwrapRecursively ( topLevelWrappers ) ;
327-
328- function unwrapRecursively ( wrapperArray : Wrapper [ ] ) {
329- const result : WithChildren < T > [ ] = [ ] ;
330- for ( const wrapper of wrapperArray ) {
331- if ( wrapper . children . length === 0 ) {
332- result . push ( wrapper . item ) ;
333- } else {
334- result . push ( {
335- ...wrapper . item ,
336- children : unwrapRecursively ( wrapper . children )
337- } ) ;
338- }
339- }
340- return result ;
341- }
342- }
0 commit comments