Введение в рекурсию

Чтобы узнать о рекурсии, нажмите здесь.

Нажмите здесь, чтобы опубликовать эту статью в LinkedIn »

Почему я только что потратил час, щелкая ссылку, которая просто возвращается на эту страницу ?!

Две причины:

  1. Это рекурсия.
  2. Надеюсь, это даст мне лучшую статистику.

Если вы не понимаете, почему это рекурсия, и действительно потратили час, нажимая на ту же ссылку, то цель этой статьи - понять эту шутку.

Зачем я это пишу?

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

Недавно я работал над проблемой, когда мне нужно было сгладить многомерные объекты в плоскую структуру. Это была проблема, которую легко разрешить с помощью рекурсии; и тот, который, как я думал, хорошо подойдет для публикации в блоге.

Итак, что мы строим?

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

Обратите внимание, что нас интересуют только ключи, с которыми на самом деле связано значение; не те, которые назначены вложенным объектам.

Функция

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

Давайте теперь пройдемся по этой строке за строкой.

  1. Создайте пустой объект, который мы добавим и вернем.
let obj = {}; 

2. Получите keys из object, переданных в функцию. Если бы у вас был объект {one: 1, two: 2, three: { four: 4 }}, то ключи были бы one, two и three (four не вложен). Затем мы используем функцию forEach для перебора каждого key.

Object.keys(object).forEach(key => {});

3. Затем проверяем тип value; если это не объект, мы добавляем его к нашему obj Object и идем дальше.

if (typeof object[key] !== 'object') {      
    obj[key] = object[key];    
}

4. На следующем этапе мы используем рекурсию. Если тип value - это объект (например, three: { four: 4 } three - это key, а его value - это объект), то мы рекурсивно вызываем функцию flattenObject, чтобы вернуть одномерный объект, который мы можем затем объединить с obj

else {      
    obj = { ...obj, ...flattenObject(object[key]) };    
}

Имеет смысл!

Нет, конечно же, нет! Даже я немного запутался. Давайте рассмотрим пункт 4 более подробно.

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

const obj = {one: 1, two: { three: 3}, four: 4}

Вот что происходит, когда мы передаем это нашей flattenObject функции

  • obj назначается пустому объекту
  • Массив возвращается из Object.keys(object) ([one, two, four])
  • Затем мы перебираем этот массив, используя forEach
  • Первый ключ (one) имеет значение 1, которое не является объектом, поэтому оно добавляется к obj.
  • obj теперь равно {one: 1}
  • Второй ключ (two) имеет значение объекта, поэтому мы собираемся рекурсивно вызывать функцию flattenObject.
  • Функция flattenObject вызывается с object[key], который в этом примере равен object[two] и, следовательно, {three: 3 }
  • Еще раз создается переменная (obj), равная пустому объекту ({}). Важно отметить, что эта переменная полностью отличается от той, которая использовалась при первом вызове этой функции; каждый вызов занимает свое собственное место в стеке вызовов и, следовательно, имеет собственную область действия.
  • Массив возвращается из Object.keys(object) ([three])
  • Затем мы перебираем этот массив, используя forEach (теперь вы должны начать видеть сходство с пунктами, указанными выше)
  • Первый ключ (three) имеет значение 3, которое не является объектом, поэтому оно добавляется к obj.
  • В массиве больше нет элементов, которые можно было бы перебирать, поэтому возвращается obj.
  • Помня, что мы вызывали функцию рекурсивно, возвращенный объект затем передается в оператор распространения.
// Changing this
obj = { ...obj, ...flattenObject(object[key]) };
// To this
obj = { ...{one: 1}, ...{three: 3} };
  • А поскольку оператор распространения объединяет два объекта вместе, obj теперь равно {one: 1, three: 3}
  • Третий ключ (four), помня, что переданный нами объект был {one: 1, two: { three: 3}, four: 4}, имеет значение 4, которое не является объектом, поэтому оно добавляется к obj.
  • obj теперь равно {one: 1, three: 3, four: 4}.
  • В массиве больше нет элементов, которые можно было бы перебирать, поэтому возвращается obj.

Быстрое предупреждение

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

Вот и все

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