реструктуризация плоского массива в древовидный массив - PullRequest
0 голосов
/ 05 мая 2020

Я пытался реструктурировать следующий плоский массив (участия, см. Ниже) в более организованную древовидную форму, чтобы я мог использовать компонент Tree grid для syncfusion. Я пробовал использовать функцию .reduce (). но, похоже, я не могу сделать правильную структуру. Я также пробовал loda sh сгруппировать их по уникальному идентификатору. в любом случае вот что кто-то на этой платформе помог продвинуться вперед: участие в стартовом массиве ниже. Имена некоторых свойств также должны быть переименованы.

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

Ответы [ 4 ]

1 голос
/ 14 мая 2020

Мы проверили вашу структуру массива. Чтобы преобразовать FlatArray в структуру TreeGrid Arrary, необходимо определить поле сопоставления для формирования иерархии родитель-потомок. Поэтому мы предлагаем вам использовать для определения поля (как показано ниже ParentId) для формирования структуры TreeGrid.

При использовании привязки самореферентных данных (плоских данных) в компоненте TreeGrid необходимо определить свойства IdMapping и ParentIdMapping для отношений иерархии.
См. Пример кода: -

let treeGridObj: TreeGrid = new TreeGrid ({dataSource: membersations, 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}, ...]});

См. Приведенную ниже структуру TreeGrid DataSourceArray: -

let участия = [{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 
    } 
    ] 

Поле идентификатора: Это поле содержит уникальные значения, используемые для идентификации узлов. Его имя присвоено свойству idMapping. Поле родительского идентификатора: это поле содержит значения, указывающие на родительские узлы. Его имя присвоено свойству 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

Пожалуйста, свяжитесь с нами, если вам понадобится дополнительная помощь

С уважением, Farveen sulthana T

1 голос
/ 05 мая 2020

Создайте class Task и позвольте ему управлять своими подзадачами. Используйте редуктор исходного массива в сочетании с классом 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;
}, []);
1 голос
/ 05 мая 2020

Это была очень интересная проблема для работы.

Мне нравится общая идея ответа от Нины Шольц, но мне действительно нужна была более общая c версия. Я придумал функцию, которая настроена примерно так:

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

(см. Ниже код, чтобы узнать, как я мог бы изменить эту конфигурацию, если бы потратил больше времени на решение этой проблемы.)

Это говорит о том, что внешний уровень вывода получает свойство с именем taskName из свойства KlasCode, группируя по всем совпадающим значениям, и называет его массивом дочерних элементов subtasks. Эти дети получают taskName от Moduleomschrijving и ModuleID из ModuleID, также называя своих детей subtasks и т. Д. Последний узел преобразует имя LESDatum в taskName, но не имеет дочерних элементов, в которые можно было бы спуститься. Все остальные имена сохранены. Я сделал предположение, что Moduleomschrijving и ModuleID всегда синхронизируются c. Если это не так, то я могу упустить что-то важное.

Реализация зависит от двух вспомогательных функций:

  • 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'} }
]

Это оставлено в качестве упражнения для читателя ...

1 голос
/ 05 мая 2020

Вы можете сгруппировать с другим массивом.

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