Ты думаешь об этом задом наперед.У вас есть один folder
в качестве ввода, и вы хотите развернуть его до списка «1004 * много папок».Это на самом деле противоположно reduce
, которое принимает в качестве входных данных много значений и возвращает одно значение.
Уменьшение также известно как fold , а обратное сгибание равно разворачиваться .unfold
принимает циклическую функцию f
и состояние init
.Наша функция задает контроллеры цикла next
, которые добавляют значение к выходу и задает следующее состояние, и done
, который сигнализирует об окончании цикла.
const unfold = (f, init) =>
f ( (value, nextState) => [ value, ...unfold (f, nextState) ]
, () => []
, init
)
const range = (m, n) =>
unfold
( (next, done, state) =>
state > n
? done ()
: next ( state // value to add to output
, state + 1 // next state
)
, m // initial state
)
console.log (range (3, 10))
// [ 3, 4, 5, 6, 7, 8, 9, 10 ]
Выше мы начнем с начального состояния числа, в данном случае m
.Как и переменная-накопитель в Redu, вы можете указать любое начальное состояние для unfold
.Ниже мы выражаем вашу программу, используя unfold
.Мы добавляем parent
, чтобы упростить выбор родителя папки
const parent = ({ parentId }) =>
data .find (f => f.id === String (parentId))
const breadcrumb = folder =>
unfold
( (next, done, f) =>
f == null
? done ()
: next ( f // add folder to output
, parent (f) // loop with parent folder
)
, folder // init state
)
breadcrumb (data[3])
// [ { id: '1.1.1', parentId: '1.1', name: 'folder1.1.1' }
// , { id: '1.1', parentId: 1, name: 'folder1.1' }
// , { id: '1', parentId: null, name: 'folder1' } ]
breadcrumb (data[4])
// [ { id: '2.1', parentId: 2, name: 'folder2.1' }
// , { id: '2', parentId: null, name: 'folder2' } ]
breadcrumb (data[0])
// [ { id: '1', parentId: null, name: 'folder1' } ]
Вы можете проверить результаты программы ниже
const data =
[ {id: "1", parentId: null, name: "folder1"}
, {id: "2", parentId: null, name: "folder2"}
, {id: "1.1", parentId: 1, name: "folder1.1"}
, {id: "1.1.1", parentId: "1.1", name: "folder1.1.1"}
, {id: "2.1", parentId: 2, name: "folder2.1"}
]
const unfold = (f, init) =>
f ( (value, state) => [ value, ...unfold (f, state) ]
, () => []
, init
)
const parent = ({ parentId }) =>
data .find (f => f.id === String (parentId))
const breadcrumb = folder =>
unfold
( (next, done, f) =>
f == null
? done ()
: next ( f // add folder to output
, parent (f) // loop with parent folder
)
, folder // init state
)
console.log (breadcrumb (data[3]))
// [ { id: '1.1.1', parentId: '1.1', name: 'folder1.1.1' }
// , { id: '1.1', parentId: 1, name: 'folder1.1' }
// , { id: '1', parentId: null, name: 'folder1' } ]
console.log (breadcrumb (data[4]))
// [ { id: '2.1', parentId: 2, name: 'folder2.1' }
// , { id: '2', parentId: null, name: 'folder2' } ]
console.log (breadcrumb (data[0]))
// [ { id: '1', parentId: null, name: 'folder1' } ]
Если вы проследите приведенное выше вычисление, вы увидите, что find
вызывается один раз для папки f
, добавляемой к выходу в процессе развертывания.Это дорогостоящая операция, и если ваш набор data
значительно большой, это может стать проблемой для вас.
Лучшим решением будет создание дополнительного представления ваших данных, структура которого лучше подходит дляэтот тип запроса.Если все, что вы делаете, это создаете Map
из f.id -> f
, вы можете уменьшить время поиска от линейного до логарифмического.
unfold
действительно мощный и подходит для широкого спектра задач.У меня есть много других ответов , полагающихся на это различными способами.Там также есть некоторые вопросы, касающиеся асинхронности.
Если вы застряли, не стесняйтесь задавать дополнительные вопросы: D