问题
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
: callsnestGroup
using the first level supplied and recursively callingnestGroups
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