【问题标题】:Creating Tree Object Dynamically from Collection with Keys使用键从集合动态创建树对象
【发布时间】:2020-06-21 17:34:23
【问题描述】:

我想根据一组对象动态创建嵌套的 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 属性。

我想要的输出树对象看起来像:

{
  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 定义嵌套结构并将原始集合中的相关对象分组为递归子项。如果有更好的方法将原始集合放入输出结构中,我会全力以赴。

【问题讨论】:

  • “更好的方法”与什么比较?到目前为止你做了什么?

标签: javascript arrays typescript tree lodash


【解决方案1】:

我创建了一个类来摄取整个集合,并使用方法 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);
    }
  }
}

【讨论】:

    【解决方案2】:
    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);
    

    工作演示https://codepen.io/bortao/pen/WNvdXpy

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-10-11
      • 2019-10-20
      • 2016-11-21
      • 2015-09-08
      • 2013-11-19
      • 1970-01-01
      • 2023-02-07
      相关资源
      最近更新 更多