Рекурсия Javascript: удалить уровень только с одним дочерним элементом

#javascript #recursion #ecmascript-6

#javascript #рекурсия #ecmascript-6

Вопрос:

Учитывая следующую структуру данных:

 [  
  {  
     "name":"root",
     "children":[  
        {  
           "name":"de",
           "children":[  
              {  
                 "name":"de",
                 "children":[
                    {  
                       "name":"project-1",
                       "children":[  
                       ]
                    },
                    {  
                       "name":"project-2",
                       "children":[  
                       ]
                    }
                 ]
              }
           ]
        }
     ]
  }
]
  

Ожидаемый:

 [
  {
     "name":"project-1",
     "children":[
     ]
  },
  {
     "name":"project-2",
     "children":[
     ]
  }
]
  

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

Я бы сделал это с reduce , но все еще не могу разобраться reduce в сочетании с рекурсией. Есть идеи?

Комментарии:

1. извините, но не могли бы вы, пожалуйста, привести пример результирующего массива, который вы хотели бы иметь? Спасибо!

2. Пожалуйста, укажите ожидаемый массив. написанный вами код и текущий результат.

3. Отредактировал ожидаемый результат в описании

Ответ №1:

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

 .map(o => o.children).flat()
  

РЕДАКТИРОВАТЬ: обновленный ответ после выяснения реального вопроса
Тем не менее, вы можете использовать map и flatten logic, но рекурсивным способом.

 function removeSingleChildElms (o) {
  if (!o.children) return

  if (o.children.length === 1) {
      return o.children.map(removeSingleChildElms).flat()
  } else {
    return o.children
  }
}
  

ПРАВКА2:
Некоторое объяснение: Проблема заключается в преобразовании массива объектов в массив различных объектов. Я не выбираю reduce, потому что проблема не заботится о взаимосвязи / логике между родственными элементами. Речь идет всего лишь о преобразовании, следовательно, map будет работать достаточно хорошо.

Проблема требует «пропустить» объекты с 1 дочерним элементом. Это повторяющаяся часть, означающая: если вы видите объект, удовлетворяющий этому условию, вы переходите к более глубокому отображению. В любом другом допустимом условии дочерние элементы остаются неизменными (в противном случае)

Комментарии:

1. Это не учитывает данные, которые имеют дочерние элементы дочерних элементов

2. Он говорит: «Я хочу иметь новый массив, который содержит только дочерние элементы «корневого» уровня без самого root.». Это удаление корневого уровня. При вводе и выводе частей ‘and so an’ остается неизменным

3. Ну, это всего лишь пример. Алгоритм должен работать для всех входных данных. И так далее указывает на то, что существуют дополнительные уровни, где этот алгоритм должен быть применен. OP также заявляет, что он хочет использовать array.reduce и recursion, указывая, что он хочет обработать все слои

4. обновлен мой ответ

Ответ №2:

Преобразование дерева можно упростить, разбив задачу на две части:

  1. функция для преобразования одного узла
  2. функция для преобразования массива узлов

Чтобы преобразовать один узел, мы пишем transform1

  • если дочерних элементов нет, мы нашли конечный узел, верните одноэлементный узел
  • если есть только один дочерний элемент, удалите узел и верните преобразование его единственного дочернего элемента
  • в противном случае узел имеет несколько дочерних элементов, вызовите нашу вторую функцию transformAll
 const transform1 = ({ children = [], ...node }) =>
  children.length === 0         // leaf
    ? [ node ]
: children.length === 1         // singleton
    ? transform1 (...children)
: transformAll (children)       // default  

Чтобы преобразовать массив узлов, мы пишем transformAll

 const transformAll = (arr = []) =>
  arr .flatMap (transform1)
  

Как вы можете видеть, transformAll вызывает transform1 , который также вызывает transformAll . Этот метод называется взаимной рекурсией, и это отличный способ обрабатывать рекурсивные структуры данных, подобные предложенной в вашем вопросе.

Чтобы убедиться, что наша функция работает должным образом, я изменил дерево, чтобы оно содержало больше сценариев данных. Обратите внимание, наша программа работает для любых узлов со children свойством. Все остальные свойства отображаются в результате —

 const data =
  [ { name: "a"
    , children:
        [ { name: "a.a"
          , children: 
              [ { name: "a.a.a"
                , children: []
                }
              , { name: "a.a.b"
                , foo: 123
                , children: []
                }
              ]
          }
        ]
    }
  , { name: "b"
    , children:
        [ { name: "b.a"
          , children: 
              [ { name: "b.a.a"
                , children: []
                }
              , { name: "b.a.b"
                , children: []
                }
              ]
          }
        , { name: "b.b"
          , children: []
          }
        ]
    }
  , { name: "c"
    , children: []
    }
  ]
  

Мы можем выполнить transformAll обработку ваших данных для преобразования всех узлов —

 transformAll (data)
// [ { name: 'a.a.a' }
// , { name: 'a.a.b', foo: 123 }
// , { name: 'b.a.a' }
// , { name: 'b.a.b' }
// , { name: 'b.b' }
// , { name: 'c' }
// ]
  

Или для преобразования одного узла мы вызываем transform1

 transform1 (data[0])
// [ { name: 'a.a.a' }
// , { name: 'a.a.b', foo: 123 }
// ]

transform1 (data[2])
// [ { name: 'c' } ]
  

Разверните приведенный ниже фрагмент, чтобы проверить результаты в вашем собственном браузере —

 const data =
  [ { name: "a"
    , children:
        [ { name: "a.a"
          , children: 
              [ { name: "a.a.a"
                , children: []
                }
              , { name: "a.a.b"
                , foo: 123
                , children: []
                }
              ]
          }
        ]
    }
  , { name: "b"
    , children:
        [ { name: "b.a"
          , children: 
              [ { name: "b.a.a"
                , children: []
                }
              , { name: "b.a.b"
                , children: []
                }
              ]
          }
        , { name: "b.b"
          , children: []
          }
        ]
    }
  , { name: "c"
    , children: []
    }
  ]

const transform1 = ({ children = [], ...node }) =>
  children.length === 0         // leaf
    ? [ node ]
: children.length === 1         // singleton
    ? transform1 (...children)
: transformAll (children)       // default

const transformAll = (arr = []) =>
  arr .flatMap (transform1)

console .log (transformAll (data))

// [ { name: 'a.a.a' }
// , { name: 'a.a.b', foo: 123 }
// , { name: 'b.a.a' }
// , { name: 'b.a.b' }
// , { name: 'b.b' }
// , { name: 'c' }
// ]