Преобразование списка в дерево (узел дерева) - JavaScript

У меня есть список (массив объектов), в котором у них могут быть родители или нет.

const categories = [
  { value: 1, text: 'Food', parent_id: null },
  { value: 2, text: 'Services', parent_id: null },
  { value: 3, text: 'Amazon Prime', parent_id: 2 },
  { value: 4, text: 'Netlix', parent_id: 2 },
  { value: 5, text: 'Beauty', parent_id: null },
  { value: 6, text: 'Education', parent_id: null },
  { value: 8, text: 'Academy', parent_id: 6 },
  { value: 9, text: 'Taxes', parent_id: null },
  { value: 10, text: 'Fees', parent_id: 9 },
  { value: 11, text: 'Recreation', parent_id: null },
  { value: 12, text: 'Pub', parent_id: 11 },
  { value: 13, text: 'House', parent_id: null },
  { value: 15, text: 'Illumination', parent_id: 13 },
  { value: 16, text: 'Internet', parent_id: 13 },
  { value: 18, text: 'Pets', parent_id: null },
  { value: 19, text: 'Award', parent_id: null },
  { value: 20, text: 'Gifts', parent_id: null },
  { value: 21, text: 'Salary', parent_id: null },
  { value: 22, text: 'Health', parent_id: null },
  { value: 23, text: 'Doctor', parent_id: 22 },
  { value: 24, text: 'Pharmacy', parent_id: 22 },
  { value: 25, text: 'Transport', parent_id: null },
  { value: 26, text: 'Trip', parent_id: null },
  { value: 27, text: 'NY', parent_id: 27 },
  { value: 28, text: 'Investment', parent_id: 9 },
  { value: 29, text: 'Bakery', parent_id: 1 },
  { value: 30, text: 'Restaurant', parent_id: 1 },
  { value: 31, text: 'Marketplace', parent_id: 1 },
  { value: 32, text: 'Rent', parent_id: 13 },
  { value: 33, text: 'Phone', parent_id: 13 },
  { value: 34, text: 'Reforms', parent_id: 13 },
  { value: 35, text: 'Telecine', parent_id: 2 },
  { value: 36, text: 'Appliances', parent_id: 13 },
  { value: 37, text: 'Disney', parent_id: 2 },  
  { value: 38, text: 'Water', parent_id: 13 },
  { value: 39, text: 'Kitchen', parent_id: 35 },
  { value: 40, text: 'Furniture', parent_id: 40 },
  { value: 41, text: 'Construction Materials', parent_id: 40 },
]

export default categories;

Мне нужно преобразовать этот список в дерево, сделав его из сравнения: если у него есть tag_parent_id, и если он равен значению какого-то другого элемента, это дочерний элемент! Результат должен быть таким:

const categories = [
  { value: 1, text: 'Food', parent_id: null, children: [
    { value: 29, text: 'Bakery', parent_id: 1 },
    { value: 30, text: 'Restaurant', parent_id: 1 },
    { value: 31, text: 'Marketplace', parent_id: 1 },
  ] },
  { value: 2, text: 'Services', parent_id: null, children: [
    { value: 3, text: 'Amazon Prime', parent_id: 2 },
    { value: 4, text: 'Netlix', parent_id: 2 },
    { value: 35, text: 'Telecine', parent_id: 2 },
    { value: 37, text: 'Dinsey', parent_id: 2 },  
  ] },
  { value: 5, text: 'Beauty', parent_id: null },
  { value: 6, text: 'Education', parent_id: null, children: [
    { value: 8, text: 'Academy', parent_id: 6 },
  ] },
  { value: 9, text: 'Taxes', parent_id: null, children: [
    { value: 28, text: 'Investment', parent_id: 9 },
    { value: 10, text: 'Fees', parent_id: 9 },
  ] },
  { value: 11, text: 'Recreation', parent_id: null, children: [
    { value: 12, text: 'Pub', parent_id: 11 },
  ] },
  { value: 13, text: 'House', parent_id: null, children: [
    { value: 15, text: 'Illumination', parent_id: 13 },
    { value: 16, text: 'Internet', parent_id: 13 },
    { value: 32, text: 'Rent', parent_id: 13 },
    { value: 33, text: 'Phone', parent_id: 13 },
    { value: 34, text: 'Reforms', parent_id: 13, children: [
      { value: 39, text: 'Kitchen', parent_id: 36, children: [
        { value: 40, text: 'Furniture', parent_id: 39 },
        { value: 41, text: 'Construction Materials', parent_id: 39 },
      ] },
    ] },
    { value: 36, text: 'Appliances', parent_id: 13 },
    { value: 38, text: 'Water', parent_id: 13 },
  ] },
  { value: 18, text: 'Pets', parent_id: null },
  { value: 19, text: 'Award', parent_id: null },
  { value: 20, text: 'Gifts', parent_id: null },
  { value: 21, text: 'Salary', parent_id: null },
  { value: 22, text: 'Health', parent_id: null, children: [
    { value: 23, text: 'Doctor', parent_id: 22 },
    { value: 24, text: 'Pharmacy', parent_id: 22 },
  ] },
  { value: 25, text: 'Transport', parent_id: null },
  { value: 26, text: 'Trip', parent_id: null, children: [
    { value: 27, text: 'NY', parent_id: 26},
  ] },
]

Я создал следующую функцию:

cascateCategories(state) {
      var end = [];
      getNestedChildren(state.categories)
      return end

      function getNestedChildren(categories) {
        for(let i in categories) {
          
          categories.forEach(category => {
            if (category.value == categories[i].parent_id) {
              
              if (!category.children) {
                category['children'] = [];
              }
              
              category.children.push(categories[i]);
              end.push(category)             
            }
          });          
          console.info(end);
        }
        return end;
      }
    },

Однако он добавляет один и тот же элемент несколько раз, а не только 1. Кроме того, он игнорирует элементы, не имеющие дочерних элементов. В конце концов, у меня проблема с кодом, и я не знаю, какой именно.

Я работаю с VueJS, Vuex и JavaScript.

Вы дважды выполняете цикл по одному и тому же массиву, сначала с for и вложенным с forEach. Это вызывает дубликаты.

trincot 08.04.2021 22:07
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
0
1
26
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Вы можете взять объект для сбора и вернуть все узлы из родительского узла.

const
    categories = [{ value: 1, text: 'Food', parent_id: null }, { value: 2, text: 'Services', parent_id: null }, { value: 3, text: 'Amazon Prime', parent_id: 2 }, { value: 4, text: 'Netlix', parent_id: 2 }, { value: 5, text: 'Beauty', parent_id: null }, { value: 6, text: 'Education', parent_id: null }, { value: 8, text: 'Academy', parent_id: 6 }, { value: 9, text: 'Taxes', parent_id: null }, { value: 10, text: 'Fees', parent_id: 9 }, { value: 11, text: 'Recreation', parent_id: null }, { value: 12, text: 'Pub', parent_id: 11 }, { value: 13, text: 'House', parent_id: null }, { value: 15, text: 'Illumination', parent_id: 13 }, { value: 16, text: 'Internet', parent_id: 13 }, { value: 18, text: 'Pets', parent_id: null }, { value: 19, text: 'Award', parent_id: null }, { value: 20, text: 'Gifts', parent_id: null }, { value: 21, text: 'Salary', parent_id: null }, { value: 22, text: 'Health', parent_id: null }, { value: 23, text: 'Doctor', parent_id: 22 }, { value: 24, text: 'Pharmacy', parent_id: 22 }, { value: 25, text: 'Transport', parent_id: null }, { value: 26, text: 'Trip', parent_id: null }, { value: 27, text: 'NY', parent_id: 27 }, { value: 28, text: 'Investment', parent_id: 9 }, { value: 29, text: 'Bakery', parent_id: 1 }, { value: 30, text: 'Restaurant', parent_id: 1 }, { value: 31, text: 'Marketplace', parent_id: 1 }, { value: 32, text: 'Rent', parent_id: 13 }, { value: 33, text: 'Phone', parent_id: 13 }, { value: 34, text: 'Reforms', parent_id: 13 }, { value: 35, text: 'Telecine', parent_id: 2 }, { value: 36, text: 'Appliances', parent_id: 13 }, { value: 37, text: 'Disney', parent_id: 2 }, { value: 38, text: 'Water', parent_id: 13 }, { value: 39, text: 'Kitchen', parent_id: 35 }, { value: 40, text: 'Furniture', parent_id: 40 }, { value: 41, text: 'Construction Materials', parent_id: 40 }],
    tree = function (data, root) {
        var t = {};
        data.forEach(o => {
            Object.assign(t[o.value] ??= {}, o);
            t[o.parent_id] ??= {};
            t[o.parent_id].children ??= [];
            t[o.parent_id].children.push(t[o.value]);
        });
        return t[root].children
    }(categories, null);

console.info(tree);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Ответ принят как подходящий

Добавляйте в результат только категории без родителя, остальные добавляйте в children их родителя:

// ....
cascateCategories(state) {
  const res = [];
  for (let category of state.categories) {
    if (category.parent_id == null) {
      res.push(category);
    } else {
      const parent = _.find(state.categories, { value: parent_id })
      if (parent.children == null) parent.children = [];
      parent.children.push(category);
    }
  }
  return res;
}

_ - это lodash. Если у вас import find from 'lodash/find', то _. можно опустить.

Если элемент с value: v всегда имеет индекс v-1, мы можем установить:

const parent = state.categories[parent_id - 1]

вместо

const parent = _.find(state.categories, { value: parent_id })

Эта функция изменит элементы во входном массиве. Он использует тот факт, что в JavaScript переменные хранят ссылки на объекты, а не на значения.

Мне понравилось ваше решение! Спасибо. Не могли бы вы объяснить мне, что происходит в этой строке: const parent = _.find (state.categories, {value: parent_id})

Grazi 09.04.2021 14:48
_.find(state.categories, { value: parent_id }) возвращает первый элемент в state.categories с item.value === parent_id
Nathan Xabedi 09.04.2021 18:15

Другие вопросы по теме