Сначала нам нужно подумать о некотором типе T
, который позволяет эффективно искать конкретную предустановку с помощью name
.Массивы не предоставляют такой возможности, поэтому мы конвертируем из Array в желаемый тип, T
.В этом случае мы будем использовать Map
-
// type preset =
// { id: number
// , name: string
// , content: string array
// , hidden: bool
// , include: string array
// }
// type t =
// (string, preset) map
Выше мы видим t
как map
, который имеет string
клавиш, каждая из которых указывает на значение preset
.Теперь мы можем написать fromArray
-
// fromArray : preset array -> t
const fromArray = (a = []) =>
a.reduce((r,x) => r.set(x.name, x), new Map)
Теперь, когда мы можем легко найти предустановку с помощью name
, мы напишем общую процедуру обхода.Это позволяет нам отделить 1) обход нашего дерева от 2) предполагаемой операции, которую мы хотим выполнить с каждым элементом дерева -
// traverse : (t, string) -> preset generator
const traverse = function* (t = new Map, name = "") {
if (!t.has(name)) return
yield* traverse1(t, t.get(name))
}
// traverse1 : (t, preset) -> preset generator
const traverse1 = function* (t = new Map, preset = {}) {
yield preset
for (const i of preset.include || [])
yield* traverse(t, i)
}
Теперь наша функция getIncludes
может быть простой программой.Ему больше не нужно заниматься обходом дерева, и вместо этого он может сосредоточиться на преобразовании линейной последовательности preset
элементов в желаемый набор строк -
const getIncludes = (t = new Map, name = "") =>
{ const r = new Set
for (const p of traverse(t, name))
if (r.has(p.name) || p.name === name)
continue
else
r.add(p.name)
return Array.from(r)
}
Как вы можете видеть, удаляя логику обходаот каждой функции, которая зависит от нашего дерева, может быть огромная помощь.Давайте проверим это здесь -
const tree =
fromArray(presets)
getIncludes(tree, "media")
// [ "videoFormats", "imageFormats", "audioFormats" ]
getIncludes(tree, "audioFormats")
// [ "imageFormats" ]
getIncludes(tree, "imageFormats")
// []
Разверните фрагмент ниже, чтобы проверить результаты в вашем собственном браузере -
const presets =
[ { id: 0
, name: "videoFormats"
, content: ["(avi|mkv|mov|mp4|mpg|wmv)"]
, hidden: true
, include: ["imageFormats"]
}
, { id: 1
, name: "audioFormats"
, content: ["(ac3|flac|m4a|mp3|ogg|wav|wma)"]
, hidden: true
, include: ["imageFormats"]
}
, { id: 2
, name: "imageFormats"
, content: ["(bmp|gif|jpg|jpeg|png|psd|tif|tiff)"]
, hidden: true
}
, { id: 3
, name: "media"
, title: "Media"
, include: ["videoFormats", "audioFormats"]
, hidden: false
}
]
const fromArray = (a = []) =>
a.reduce((r,x) => r.set(x.name, x), new Map)
const traverse = function* (t = new Map, name = "") {
if (!t.has(name)) return
yield* traverse1(t, t.get(name))
}
const traverse1 = function* (t = new Map, preset = {}) {
yield preset
for (const i of preset.include || [])
yield* traverse(t, i)
}
const getIncludes = (t = new Map, name = "") =>
{ const r = new Set
for (const p of traverse(t, name))
if (r.has(p.name) || p.name === name)
continue
else
r.add(p.name)
return Array.from(r)
}
const tree =
fromArray(presets)
console.log(getIncludes(tree, "media"))
// [ "videoFormats", "imageFormats", "audioFormats" ]
console.log(getIncludes(tree, "audioFormats"))
// [ "imageFormats" ]
console.log(getIncludes(tree, "imageFormats"))
// []