[英]Creating Tree Object Dynamically from Collection with Keys
我想根據一組對象動態創建嵌套的 Tree 對象,這些對象可以具有由鍵列表指定的樹的級別。
我的集合可能看起來像[{a_id: '1', a_name: '1-name', b_id: '2', b_name: '2_name', c_id: '3', c_name: '3_name'}...]
我的鍵對應於樹的級別,可能如下所示: ['a', 'b', 'c']
頂級對象有一個名為options
的屬性,用於存儲{key: any, value: any}
。 所有子項都相同,但它嵌套在名為groups
的數組中,該數組具有引用其父選項值的group
屬性。
我想要的輸出 Tree 對象看起來像:
{
id: 'a',
options: [{ key: '1-name', value: '1'}...],
child: {
id: 'b',
groups: [
{ group: '1', name: '1-name', options: [{key: '2-name', value: '2'}]}
],
child: {
id: 'c',
groups: [
{ group: '2', name: '2-name', options: [{key: '3-name', value: '3'}]}
],
}
}
}
我很難編寫一個簡潔的函數來從基本集合中構建這個遞歸結構。 我基本上想要keys
來定義嵌套結構並將原始集合中的相關對象分組為遞歸子項。 如果有更好的方法將原始集合放入輸出結構中,我會全力以赴。
function process(keys, data, pgroup, pname) {
if (keys.length == 0) return;
var key = keys.shift(); // Get first element and remove it from array
var result = {id: key};
var group = data[key + "_id"];
var name = data[key + "_name"];
var options = [{key: name, value: group}];
if (pgroup) { // pgroup is null for first level
result.groups = [{
group: pgroup,
name: pname,
options: options
}];
} else {
result.options = options;
}
if (keys.length > 0) { // If havent reached the last key
result.child = process(keys, data, group, name);
}
return result;
}
var result = process (
['a', 'b', 'c'],
{a_id: '1', a_name: '1-name', b_id: '2', b_name: '2_name', c_id: '3', c_name: '3_name'}
);
console.log(result);
我創建了一個攝取整個集合的類,並使用 export 方法以我在原始問題中請求的格式獲取數據:
import { Option, OptionGroup, SelectTreeFieldOptions } from '@common/ui';
import { isObject } from 'lodash';
// Symbol used for Key in Group
const KEY_SYMBOL = Symbol('key');
/**
* Tree class used to create deeply nested objects based on provided 'level'
*/
export class Tree {
private readonly level: string;
private readonly groups: Record<string, Record<string, any>>;
private readonly child?: Tree;
constructor(private readonly levels: string[], private readonly isRoot = true) {
this.level = levels[0];
this.groups = isRoot && { root: {} } || {};
if (levels.length > 1) {
this.child = new Tree(levels.slice(1), false);
}
}
/**
* Set Tree Data with Collection of models
* Model should have two properties per-level: ${level}_id and ${level}_name
* Each entry is matched to the level Tree's groups and then passed down to its children
* @param data - the Collection of data to populate the Tree with
*/
setData(data: Record<string, any>[]): Tree {
data.forEach(entry => this.addData(entry, 'root', ''));
return this;
}
export() {
const key = this.isRoot && 'options' || 'groups';
const values = this.getOptionsOrGroups();
return {
id: `${this.level}s`,
[key]: values,
...(this.child && { child: this.child.toSelectTreeField() } || {}),
};
}
/**
* Retrieve recursive Option or OptionGroups from each level of the Tree
* @param nestedObject - optional sub-object as found on a child branch of the Tree
*/
protected getOptionsOrGroups(nestedObject?: Record<string, any>): (Option | OptionGroup)[] {
const options = (this.isRoot && this.groups.root) || nestedObject || this.groups;
return Object.entries(options).map(([val, key]) => {
const value = Number(val) || val;
if (!isObject(key)) {
return { key, value };
}
return { value, group: key[KEY_SYMBOL], options: this.getOptionsOrGroups(key) };
}) as (Option | OptionGroup)[];
}
/**
* Try to add data from a collection item to this level of the Tree
* @param data - the collection item
* @param parentId - ID of this item's parent if this Tree is a child
* @param parentName - label used for the item's parent if this Tree is a child
*/
protected addData(data: Record<string, any>, parentId: string, parentName: string) {
const id = data[`${this.level}_id`];
const name = data[`${this.level}_name`];
if (!id || !name) {
throw new Error(`Could not find ID or Name on level ${this.level}`);
}
(this.groups[parentId] || (this.groups[parentId] = { [KEY_SYMBOL]: parentName }))[id] = name;
if (this.child) {
this.child.addData(data, id, name);
}
}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.