Рекурсивно добавлять данные к объекту динамически - PullRequest
2 голосов
/ 05 мая 2020

Имеется следующий код:

const data = {
  name: 'product',
  children: [
    { name: 'title' },
    { name: 'code' },
    { name: 'images', children: [{ name: 'image1' }, { name: 'image2' }] },
  ],
}


let result = {}

function resolveNodes(nodes, parentNode){
  if(nodes.children){
    parentNode = nodes.name;
    result[parentNode] = {};
    nodes.children.forEach(node => resolveNodes(node, parentNode));
  }else{
    result[parentNode][nodes.name] = nodes.name;
  }
}


resolveNodes(data);

console.log(result);

Текущий выход

{
 product: {
 title:"title",
 code:"code"
},
images: {
 image1:"image1",
 image2:"image2"
}
}

Желаемый результат:

{
 product: {
 title:"title",
 code:"code",
 images: {
  image1:"image1",
  image2:"image2"
  }
 }
}

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

Эта функция должна будет обрабатывать как минимум один дополнительный уровень дочерних элементов, но я только что добавил 2, чтобы упростить вопрос.

Как лучше всего это реализовать?

1 Ответ

3 голосов
/ 06 мая 2020

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

Вместо того, чтобы давать упрощенное решение, давайте подробнее рассмотрим code.

resolveNodes = ({ name, children }) => ({ [name]: children
    ? Object.assign(...children.map(resolveNodes))
    : name
})

Первая часть,

resolveNodes = ({ name, children }) =>
                ^^^^^^^^^^^^^^^^^^

аргументы функции, содержит деструктурирующее присваивание , где ожидается объект и от этого объекта , названные свойства принимаются как собственные переменные.

Следующее возвращаемое выражение

resolveNodes = ({ name, children }) => ({ 

})

содержит объект с

                                          vvvvvvv
resolveNodes = ({ name, children }) => ({ [name]:


})

вычисляемым именем свойства , который принимает значение переменной как имя свойства.

Следующий условный (тернарный) оператор ?:

                                                  children
    ? Object.assign(...children.map(resolveNodes))
    : name

проверяет children.

Если это значение правдиво , как массив, он оценивает часть после ?, и если значение ложь , противоположно правда , то принимает выражение после :.

Короче говоря, если нет дочерних элементов, тогда возьмите name в качестве значения свойства.

Часть для правды, если дочерние элементы существуют,

      Object.assign(...children.map(resolveNodes))

возвращает один объект со всеми дочерними элементами properties.

      Object.assign(...children.map(resolveNodes))
      ^^^^^^^^^^^^^                                create a single object
                    ^^^                            spread syntax
                        ^^^^^^^^^^^                return all element by using
                                    ^^^^^^^^^^^^   the actual function again

Эту часть легче понять, если следовать порядку выполнения.

                       children.map(resolveNodes)

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

[
    { title: 'title' },
    { code: 'code' },
    { images:                  // this looks different, because nested 
        {                      // children are called first
            image1: 'image1',
            image2: 'image2'
        }
    }
]

и содержит объекты с одним свойством.

Чтобы получить один объект со всеми свойствами массива, Object.assign принимает объекты и объединяет все объекты в один и заменяет те же свойства в целевом объекте последним следующим свойством с таким же именем. Здесь проблема не в этом, потому что все свойства разные. На самом деле проблема заключается в том, чтобы принять массив в качестве параметров для вызова функции.

У этой проблемы есть два решения, одно - это старый вызов функции с Function#apply, например

Object.assign.apply(null, children.map(resolveNodes))

Другой и используемый

Object.assign(...children.map(resolveNodes))

- это синтаксис распространения ..., который принимает итерацию и добавляет элементы в качестве параметров в вызов функции.

const
    resolveNodes = ({ name, children }) => ({ [name]: children
        ? Object.assign(...children.map(resolveNodes))
        : name
    }),
    data = { name: 'product', children: [{ name: 'title' }, { name: 'code' }, { name: 'images', children: [{ name: 'image1' }, { name: 'image2' }] }] },
    result = resolveNodes(data);

console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
...