flat array restructure to a tree array

独自空忆成欢 提交于 2020-06-17 13:29:58

问题


I have been trying to restructure the following flat array (participations, see below) to a more organized tree form, so that i can use Tree grid component of syncfusion. I have tried using the .reduce() function. but it seems i cannot make the correct strucutre. I have also tried lodash to group them by unique id. anyways here is what someone in this platform has helped to move forward: The Starting array participations is below. The names of some properties need to be renamed as well.

//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;
}, {});

Below you can find the correct data structure a custom function should make it look. Thakns anyone seeing this

//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"
          }
        ],
      }
    ]
  }]
}]

回答1:


You could group with another array.

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; }



回答2:


Create a class Task and let it manage its subtasks. Use the reducer on the original array in combination with the Task class to create your desired structure. Check the stackblitz link for the solution 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:


This was a very interesting problem to work on.

I like the overall idea of the answer from Nina Scholz, but I really wanted a more generic version. What I came up with is a function that's configured with something like the following:

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

(See below the code for how I might alter this configuration if I spent more time on this problem.)

This says that the outer level of the output gets a property named taskName from the KlasCode property, grouping on all values that match, and names it array of children subtasks. Those children get taskName from Moduleomschrijving and ModuleID from ModuleID, also naming its children subtasks, and so forth. The final node transforms the name LESDatum to taskName, but has no children to descend into. All remaining names are kept intact. I've made the assumtion that Moduleomschrijving and ModuleID are always in sync. If this is not true, then I may be missing something important.

The implementation depends on two helper functions:

  • groupBy turns an array into an object with keys the result of your custom function and their values arrays of those original element that generate the key.
  • omit creates a copy of an object with the given keys missing.

Such functions are available in many utility libraries. We also have two main functions.

  • nestGroup: takes one of those configuration objects and an array of objects, doing the key transfomation, property renaming, and child nesting. This is a useful function in its own right, useful if you only have one level of nesting.

  • nestGroups: calls nestGroup using the first level supplied and recursively calling nestGroups with the remaining configuration levels on the array of children. It bottoms out when there are no levels remaining, and just returns the array intact.

Finally, that last function is curried, so we can create a reusable function that embeds our configuration and just takes the array as a parameter. This might or might not be useful for the OP, but I can see it helpful in other places. We take advantage of this by calling

const nestParticipations = nestGroups (config)
// ... later
const tree = nestParticipations (participations)

But we could also just do

const tree = nestGroups (config) (participations)

You can see it in action here:

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}

If I wanted to spend more time on this, I think I would break this apart a bit further, and I would probably use a configuration more like this:

[
  { 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'} }
]

That is left as an exercise for the reader...




回答4:


We have checked your Array Structure, In order to convert FlatArray to TreeGrid Arrary structure, it is necessary to define Mapping Field to form parent-child hierarchy. So we suggest you to use to define Field (as like given below ParentId) to form TreeGrid structure.

While using Self-Referential Data binding (Flat Data )in TreeGrid component it is necessary to define the IdMapping and ParentIdMapping property for the hierarchy relations.
Refer to the code example:-

let treeGridObj: TreeGrid = new TreeGrid({ dataSource: participations, idMapping: 'LESID', parentIdMapping: 'ParentId', allowPaging: true, treeColumnIndex: 1, columns: [ { field: 'LESID', headerText: 'Task ID', width: 90, textAlign: 'Right' }, { field: 'ParticipationLetterCode', headerText: 'Task Name', width: 180 }, . . . ] });

Refer to the below TreeGrid DataSourceArray Structure:-

let participations = [{ KlasCode: "1S RD BJ GS ma-d", LESDatum: "12/12/20", LESID: 1, ModuleID: "1050", Moduleomschrijving: "Realisaties blouse/jurk", ParticipationLetterCode: "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 Field: This field contains unique values used to identify nodes. Its name is assigned to the idMapping property. Parent ID Field: This field contains values that indicate parent nodes. Its name is assigned to the parentIdMapping property.

Refer to the documentation and Demo Link:- https://ej2.syncfusion.com/demos/#/material/tree-grid/selfreference.html https://ej2.syncfusion.com/documentation/treegrid/data-binding/#self-referential-data-binding-flat-data

Please get back to us if you need any further assistance

Regards, Farveen sulthana T



来源:https://stackoverflow.com/questions/61610032/flat-array-restructure-to-a-tree-array

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!