【问题标题】:flat array restructure to a tree array平面数组重组为树数组
【发布时间】:2020-08-19 22:19:31
【问题描述】:

我一直在尝试将以下平面数组(参与,见下文)重组为更有条理的树形式,以便我可以使用同步融合的树网格组件。我试过使用 .reduce() 函数。但似乎我无法做出正确的结构。我也尝试过 lodash 按唯一 ID 对它们进行分组。无论如何,这是这个平台上的某个人帮助前进的: 起始阵列参与如下。 一些属性的名称也需要重命名。

//what ive tried so far 
const custommodifier = (participations) => participations.reduce((a,{KlasCode, LESDatum, LESID, Moduleomschrijving,ParticipationLetterCode}) => {

    if (a[KlasCode] ){
      if (a[ParticipationLetterCode] ){
      a[KlasCode].subtasks[0].subtasks[0].subtasks.push({
          // ParticipationLetterCode,
          taskName: LESDatum,
          LESID,
        })
      } else {
        // a[KlasCode].subtasks[0].subtasks[0].taskName = ParticipationLetterCode
        a[KlasCode].subtasks[0].subtasks.push({
            taskName: ParticipationLetterCode,
            subtasks: [{
              taskName: LESDatum,
            }]
        })
      }
    } else {
      a[KlasCode] = {
        taskName: KlasCode,
        subtasks: [{
          taskName:Moduleomschrijving,
          subtasks: [{
            taskName: ParticipationLetterCode,
            subtasks: [{
              // ParticipationLetterCode,
              taskName: LESDatum,
              LESID,
            }]
          }]
        }]
      }
    }
    return a;
}, {});

您可以在下面找到自定义函数应使其看起来的正确数据结构。 谢谢看到这个的人

//starting point
let participations = [{
    KlasCode: "1S RD BJ GS ma-d",
    LESDatum: "12/12/20",
    LESID: "1",
    ModuleID: "1050",
    Moduleomschrijving:"Realisaties blouse/jurk",
    ParticipationLetterCode: "X"
  }, {
    KlasCode: "1S RD BJ GS ma-d",
    LESDatum: "11/11/20",
    LESID: "2",
    ModuleID: "1050",
    Moduleomschrijving:"Realisaties blouse/jurk",
    ParticipationLetterCode: "X",
  },
  {
    KlasCode: "1S RD BJ GS ma-d",
    LESDatum: "1/1/20",
    LESID: "3",
    ModuleID: "1050",
    Moduleomschrijving:"Realisaties blouse/jurk",
    ParticipationLetterCode: "Y"
  },
  {
    KlasCode: "2S RD BJ RR ma-d",
    LESDatum: "5/12/20",
    LESID: "4",
    ModuleID: "1051",
    Moduleomschrijving:"Realisaties shirts",
    ParticipationLetterCode: "Z"
  },
  {
    KlasCode: "2S RD BJ RR ma-d",
    LESDatum: "6/11/20,
    LESID: "4",
    ModuleID: "1051",
    Moduleomschrijving:"Realisaties shirts",
    ParticipationLetterCode: "Z"
  }
]

// Need to make the data look like this including field name change: 
let participations = [{
  "taskName": "1S RD BJ GS ma-d",
  "subtasks": [{
    "ModuleID": "1050",
    "taskName": "Realisaties blouse/jurk",
    "subtasks": [{
        "taskName": "X",
        "subtasks": [{
            "taskName": "12/12/20",
            "LESID": "1",
          },
          {
            "taskName": "11/11/20",
            "LESID": "2",
          }
        ],
      },
      {
        "taskName": "Y",
        "subtasks": [{
          "taskName": "1/1/20",
          "LESID": "3",
        }]
      }
    ]

  }]
},
{
  "taskName": "2S RD BJ RR ma-d",
  "subtasks": [{
    "ModuleID": "1051",
    "taskName": "Realisaties shirts",
    "subtasks": [{
        "taskName": "Z",
        "subtasks": [{
            "taskName": "5/12/20",
            "LESID":"4"
          },
          {
            "taskName": "6/11/20",
            "LESID":"5"
          }
        ],
      }
    ]
  }]
}]

【问题讨论】:

    标签: javascript arrays vue.js lodash syncfusion


    【解决方案1】:

    您可以与另一个数组进行分组。

    let data = [{ KlasCode: "1S RD BJ GS ma-d", LESDatum: "12/12/20", LESID: "1", ModuleID: "1050", Moduleomschrijving: "Realisaties blouse/jurk", ParticipationLetterCode: "X" }, { KlasCode: "1S RD BJ GS ma-d", LESDatum: "11/11/20", LESID: "2", ModuleID: "1050", Moduleomschrijving: "Realisaties blouse/jurk", ParticipationLetterCode: "X" }, { KlasCode: "1S RD BJ GS ma-d", LESDatum: "1/1/20", LESID: "3", ModuleID: "1050", Moduleomschrijving: "Realisaties blouse/jurk", ParticipationLetterCode: "Y" }, { KlasCode: "2S RD BJ RR ma-d", LESDatum: "5/12/20", LESID: "4", ModuleID: "1051", Moduleomschrijving: "Realisaties shirts", ParticipationLetterCode: "Z" }, { KlasCode: "2S RD BJ RR ma-d", LESDatum: "6/11/20", LESID: "4", ModuleID: "1051", Moduleomschrijving: "Realisaties shirts", ParticipationLetterCode: "Z" }],
        groups = [['KlasCode'], ['Moduleomschrijving', 'ModuleID'], ['ParticipationLetterCode'], ['LESDatum']],
        result = data
            .reduce((r, o) => {
                groups.reduce((p, [key, ...levelKeys]) => {
                    let taskName = o[key],
                        temp = (p.subtasks = p.subtasks || []).find(q => q.taskName === taskName);
                    if (!temp) {
                        let moreProps = levelKeys.reduce((t, k) => ({ ...t, [k]: o[k] }), {});
                        p.subtasks.push(temp = { ...moreProps, taskName });
                    }
                    return temp;
                }, r)
                return r;
            }, { subtasks: [] })
            .subtasks;
    
    console.log(result);
    .as-console-wrapper { max-height: 100% !important; top: 0; }

    【讨论】:

    • 快到了!最内部的对象如何(例如:结果[0].subtasks[0].subtasks[0].subtasks[0].LESDatum):如何将“LESDatum”属性名称更改为“taskName”
    【解决方案2】:

    创建一个class Task 并让它管理它的子任务。 将原始数组上的 reducer 与 Task 类结合使用来创建所需的结构。 检查stackblitz链接以获取解决方案 https://stackblitz.com/edit/js-vvxkve

    class Task {
        constructor ( taskName ) {
            this.taskName = taskName;
            this.subtasks = [];
        }
        addSubTask ( options ) {
            const ModuleID = options['ModuleID'] || null;
            const taskName = options['Moduleomschrijving'] || null;
            const participationLetterCode = options['ParticipationLetterCode'] || null;
            const subTask = this.subtasks.find ( s => s.ModuleID === ModuleID );
            const subTaksL2 = {
                taskName: options['LESDatum'] || null,
                LESID: options['LESID'] || null
            } 
            if ( !subTask ) {
                subTask = {
                    ModuleID,
                    taskName,
                    subtasks: [{
                        taskName: participationLetterCode,
                        subtasks: [ subTaksL2 ]
                    }]
                }
                this.subtasks.push ( subTask );
            } else {
                let subTaskL1 = subTask.subtasks.find ( s => s.taskName === participationLetterCode );
                if ( !subTaskL1 ) {
                    subTaskL1 = {
                        taskName: participationLetterCode,
                        subtasks: []
                    }
                    subTask.subtasks.push ( subTaskL1 );
                } 
                subTaskL1.subtasks.push ( subTaksL2 );
            }
        }
    }
    
    let participations = [{
        KlasCode: "1S RD BJ GS ma-d",
        LESDatum: "12/12/20",
        LESID: "1",
        ModuleID: "1050",
        Moduleomschrijving:"Realisaties blouse/jurk",
        ParticipationLetterCode: "X"
      }, {
        KlasCode: "1S RD BJ GS ma-d",
        LESDatum: "11/11/20",
        LESID: "2",
        ModuleID: "1050",
        Moduleomschrijving:"Realisaties blouse/jurk",
        ParticipationLetterCode: "X",
      },
      {
        KlasCode: "1S RD BJ GS ma-d",
        LESDatum: "1/1/20",
        LESID: "3",
        ModuleID: "1050",
        Moduleomschrijving:"Realisaties blouse/jurk",
        ParticipationLetterCode: "Y"
      },
      {
        KlasCode: "2S RD BJ RR ma-d",
        LESDatum: "5/12/20",
        LESID: "4",
        ModuleID: "1051",
        Moduleomschrijving:"Realisaties shirts",
        ParticipationLetterCode: "Z"
      },
      {
        KlasCode: "2S RD BJ RR ma-d",
        LESDatum: "6/11/20",
        LESID: "4",
        ModuleID: "1051",
        Moduleomschrijving:"Realisaties shirts",
        ParticipationLetterCode: "Z"
      }
    ];
    
    participations = participations.reduce ( ( acc, cval ) => {
          const taskName = cval['KlasCode'] || null;
          let node = acc.find ( a => a.taskName === taskName );
          if ( !node ) {
              node = new Task ( taskName );
              acc.push ( node );
          }
          node.addSubTask ( cval );
          return acc;
    }, []);
    

    【讨论】:

      【解决方案3】:

      这是一个非常有趣的问题。

      我喜欢 Nina Scholz 的总体思路,但我真的想要一个更通用的版本。我想出的是一个功能,配置如下:

      [
        {_children: 'subtasks', taskName: 'KlasCode'},
        {_children: 'subtasks', taskName: 'Moduleomschrijving', ModuleID: 'ModuleID'},
        {_children: 'subtasks', taskName: 'ParticipationLetterCode'},
        {taskName: 'LESDatum'},
      ]
      

      (如果我在这个问题上花费更多时间,请参阅下面的代码,了解如何更改此配置。)

      这表示输出的外层从KlasCode 属性中获取一个名为taskName 的属性,对所有匹配的值进行分组,并将其命名为子数组subtasks。这些孩子从Moduleomschrijving 获得taskName,从ModuleID 获得ModuleID,也将其孩子命名为subtasks,等等。最后一个节点将名称 LESDatum 转换为 taskName,但没有子节点可以下降。所有剩余的名称都保持不变。我假设ModuleomschrijvingModuleID 总是同步的。如果这不是真的,那么我可能会遗漏一些重要的东西。

      实现依赖于两个辅助函数:

      • groupBy 将一个数组转换为一个对象,其中包含您的自定义函数的结果以及生成该键的原始元素的值数组。
      • omit 创建一个缺少给定键的对象的副本。

      许多实用程序库中都提供了此类功能。我们还有两个主要功能。

      • nestGroup:采用其中一个配置对象和一组对象,进行键转换、属性重命名和子嵌套。这本身就是一个有用的功能,如果您只有一层嵌套,这很有用。

      • nestGroups:使用提供的第一个级别调用 nestGroup,并递归调用 nestGroups 并使用子数组上的其余配置级别。当没有剩余级别时,它会触底,并原封不动地返回数组。

      最后,最后一个函数被柯里化了,所以我们可以创建一个可重用的函数来嵌入我们的配置,并将数组作为参数。这对 OP 可能有用也可能没用,但我可以看到它在其他地方有用。我们通过调用

      来利用这一点
      const nestParticipations = nestGroups (config)
      // ... later
      const tree = nestParticipations (participations)
      

      但我们也可以这样做

      const tree = nestGroups (config) (participations)
      

      您可以在这里看到它的实际效果:

      const groupBy = (fn) => (xs) => 
        xs .reduce((a, x) => ({... a, [fn(x)]: [... (a [fn (x)] || []), x]}), {})
      
      const omit = (keys) => (obj) => 
        Object .fromEntries (Object .entries (obj) .filter (([k, v]) => !keys.includes(k)))
      
      const nestGroup = (level) => {
        const {_children, ...rest} = level
        const keys = Object .values (rest)
        const pairs = Object .entries (rest)
        return (xs) => 
          Object .values (groupBy (x => keys .map (k => x [k]) .join ('|')) (xs))
            .map (group => ({
              ... (Object .assign (... (pairs .map (([k, v]) => ({[k]: group [0] [v] }))))),
              ... (_children ? {[_children]: group .map (omit (keys))} : {... omit (keys) (group [0])})
            }))
      }
      
      const nestGroups = ([level = undefined, ... levels]) => (xs) =>
        level == undefined
          ? xs
          : nestGroup (level) (xs)  
              .map (({[level._children]: childGroup, ... rest}) => ({
                  ... rest, 
                  ... (childGroup ? {[level._children]: nestGroups (levels) (childGroup)} : {})
              })) 
      
      const config = [
        {_children: 'subtasks', taskName: 'KlasCode'},
        {_children: 'subtasks', taskName: 'Moduleomschrijving', ModuleID: 'ModuleID'},
        {_children: 'subtasks', taskName: 'ParticipationLetterCode'},
        {taskName: 'LESDatum'},
      ]
      
      const nestParticipations = nestGroups (config)
      
      let participations = [{ KlasCode: "1S RD BJ GS ma-d", LESDatum: "12/12/20", LESID: "1", ModuleID: "1050", Moduleomschrijving:"Realisaties blouse/jurk", ParticipationLetterCode: "X" }, { KlasCode: "1S RD BJ GS ma-d", LESDatum: "11/11/20", LESID: "2", ModuleID: "1050", Moduleomschrijving:"Realisaties blouse/jurk", ParticipationLetterCode: "X" }, { KlasCode: "1S RD BJ GS ma-d", LESDatum: "1/1/20", LESID: "3", ModuleID: "1050", Moduleomschrijving:"Realisaties blouse/jurk", ParticipationLetterCode: "Y" }, { KlasCode: "2S RD BJ RR ma-d", LESDatum: "5/12/20", LESID: "4", ModuleID: "1051", Moduleomschrijving:"Realisaties shirts", ParticipationLetterCode: "Z" }, { KlasCode: "2S RD BJ RR ma-d", LESDatum: "6/11/20", LESID: "4", ModuleID: "1051", Moduleomschrijving:"Realisaties shirts", ParticipationLetterCode: "Z" } ]
      
      console .log (
        nestParticipations (participations)
      )
      .as-console-wrapper {min-height: 100% !important; top: 0}

      如果我想在这方面花更多时间,我想我会进一步分解它,我可能会使用更像这样的配置:

      [
        { children: 'subtasks', matchOn: [ 'KlasCode' ], rename: { KlasCode: 'taskName' } },
        {
          children: 'subtasks', 
          matchOn: [ 'Moduleomschrijving', 'ModuleID' ], 
          rename: { Moduleomschrijving: 'taskName' }
        },
        {
          children: 'subtasks', 
          matchOn: [ 'ParticipationLetterCode' ],
          rename: { ParticipationLetterCode: 'taskName' }
        },
        { rename: {LESDatum, 'taskName'} }
      ]
      

      留给读者作为练习......

      【讨论】:

        【解决方案4】:

        我们检查了您的数组结构,为了将 FlatArray 转换为 TreeGrid 数组结构,需要定义 Mapping Field 以形成父子层次结构。因此我们建议您使用定义字段(如下面的 ParentId)来形成 TreeGrid 结构。

        在 TreeGrid 组件中使用自引用数据绑定(平面数据)时,需要为层次关系定义 IdMapping 和 ParentIdMapping 属性。
        参考代码示例:-

        让 treeGridObj: TreeGrid = new TreeGrid({ 数据来源:参与, idMapping: 'LESID', parentIdMapping: 'ParentId', 允许分页:真, 树列索引:1, 列: [ { field: 'LESID', headerText: 'Task ID', width: 90, textAlign: 'Right' }, { 字段:'ParticipationLetterCode',headerText:'任务名称',宽度:180 }, . . . ] });

        参考下面的TreeGrid DataSourceArray 结构:-

        让参与 = [{ KlasCode: "1S RD BJ GS ma-d", LESDatum:“20 年 12 月 12 日”, 莱斯德:1, 模块ID:“1050”, Moduleomschrijving:“Realisaties blouse/jurk”, 参与字母代码:“X”, ParentId = null

            }, { 
                KlasCode: "1S RD BJ GS ma-d", 
                LESDatum: "11/11/20", 
                LESID: 2, 
                ModuleID: "1050", 
                Moduleomschrijving: "Realisaties blouse/jurk", 
                ParticipationLetterCode: "X", 
                ParentId = 1                               //  Here ParentId(ParentIdMapping) value with 1 has been grouped under LESID(IdMapping) with Value 1 
            }, 
            { 
                KlasCode: "1S RD BJ GS ma-d", 
                LESDatum: "1/1/20", 
                LESID: 3, 
                ModuleID: "1050", 
                Moduleomschrijving: "Realisaties blouse/jurk", 
                ParticipationLetterCode: "Y", 
                ParentId = 1 
            }, 
            { 
                KlasCode: "2S RD BJ RR ma-d", 
                LESDatum: "5/12/20", 
                LESID: 4, 
                ModuleID: "1051", 
                Moduleomschrijving: "Realisaties shirts", 
                ParticipationLetterCode: "Z", 
                ParentId = null 
            }, 
            { 
                KlasCode: "2S RD BJ RR ma-d", 
                LESDatum: "6/11/20", 
                LESID: 5, 
                ModuleID: "1051", 
                Moduleomschrijving: "Realisaties shirts", 
                ParticipationLetterCode: "Z", 
                ParentId = 4 
            } 
            ] 
        

        ID 字段:此字段包含用于标识节点的唯一值。它的名称被分配给 idMapping 属性。 父 ID 字段:此字段包含指示父节点的值。其名称分配给 parentIdMapping 属性。

        参考文档和演示链接:- https://ej2.syncfusion.com/demos/#/material/tree-grid/selfreference.html https://ej2.syncfusion.com/documentation/treegrid/data-binding/#self-referential-data-binding-flat-data

        如果您需要任何进一步的帮助,请与我们联系

        问候, 法文苏丹娜 T

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2016-09-25
          • 2013-08-31
          • 1970-01-01
          • 1970-01-01
          • 2019-01-01
          • 1970-01-01
          相关资源
          最近更新 更多