Last active
May 9, 2018 09:42
-
-
Save wilsoncook/0d5e2bb6f3d778640c3c97d807a043e0 to your computer and use it in GitHub Desktop.
Recursive a array, support addson operations: filter, map, restore
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Recursively iterate table data rows, if iterator() returns false, then will stop iterating | |
* @param rows Data list which may contain children | |
* @param iterator Iterator, return false to stop iterating | |
* @param childrenField Specify the children field's key | |
* @param _parent [Private] The parent of the rows, null by default, used for iterator() ONLY | |
* @return If iterated all items, this method will return true | |
*/ | |
function recurseRows<T>( | |
rows: T[], | |
iterator: (row: T, index: number, rows: T[], parent: T) => boolean | any, | |
childrenField = 'children', | |
_parent: T = null | |
) { | |
if (!rows) { return false; } | |
for (let i = 0, len = rows.length; i < len; i++) { | |
const row = rows[i]; | |
if (iterator(row, i, rows, _parent) === false) { return false; } | |
if (row[childrenField] && recurseRows(row[childrenField], iterator, childrenField, row) === false) { | |
return false; | |
} | |
} | |
return true; | |
} | |
/** | |
* Returns a new array which only contains the row that filtered (Will keep the tree structure) | |
* [NOTE] All tree array(children) will be modified, ONLY the row object will be kept as original | |
* @param rows | |
* @param filter Return `true` to keep this row | |
* @param options.childrenField | |
* @param options.backupChildren The backup field for children, stored in row, if not specified, will not do backup | |
*/ | |
function recurseFilterRows<T>(rows: T[], filter: (row: T) => boolean, options?: RecurseFilterRowsOptions) { | |
options = Object.assign({ | |
childrenField: 'children', | |
backupChildren: null | |
}, options); | |
if (!rows) { return rows; } | |
const copyRows: T[] = [], { childrenField, backupChildren } = options; | |
for (let i = 0, len = rows.length; i < len; i++) { | |
// const row = Object.assign({}, rows[i]); | |
const row = rows[i]; | |
if (backupChildren) { row[backupChildren] = row[childrenField]; } // Backup | |
let filterHasChildren = false; | |
if (row[childrenField]) { | |
row[childrenField] = this.recurseFilterRows(row[childrenField], filter, options); | |
filterHasChildren = row[childrenField].length > 0; | |
} | |
if (filterHasChildren || filter(row)) { | |
copyRows.push(row); | |
} | |
} | |
return copyRows; | |
} | |
// Restore children of the rows that being backup by recurseFilterRows() | |
function recurseRestoreChildren<T>(rows: T[], backupField: string, childrenField = 'children') { | |
this.recurseRows(rows, (row) => { | |
if (typeof row[backupField] !== 'undefined') { | |
row[childrenField] = row[backupField]; | |
row[backupField] = undefined; | |
} | |
}); | |
return rows; | |
} | |
/** | |
* Recursively map table data rows | |
* @param rows Data list which may contain children | |
* @param callback Map iterator, MUST return an object, [NOTE] the "obj[childrenField]" will be overriden | |
* @param options.childrenField Specify the children field's key, this field MUST be an array | |
* @param options.toChildrenField Use new field key for children, if null, will use options.childrenField by default | |
*/ | |
function recurseMap<T>(rows: T[], callback: (row: T, index: number, rows: T[]) => any, options?: RecurseMapOptions) { | |
options = Object.assign({ | |
childrenField: 'children', | |
toChildrenField: null, | |
holdEmptyChildren: false, | |
}, options); | |
if (!options.toChildrenField) { options.toChildrenField = options.childrenField; } | |
return rows.map((row, index, arr) => { | |
const resultRow = callback(row, index, arr); | |
if (Array.isArray(row[options.childrenField])) { | |
resultRow[options.toChildrenField] = this.recurseMap(row[options.childrenField], callback, options); | |
} else if (options.holdEmptyChildren) { | |
resultRow[options.toChildrenField] = []; | |
} | |
return resultRow; | |
}); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment