我正在创建一个预设系统,其中预设可以“包含”其他预设。
Preset A包括Preset B和C,而Preset B包括D和E。
它们都有这样的结构: - ID - 名称(字符串,在包含中用作引用) - 内容(字符串数组) - include (name数组,对应name prop)
内容就是包含的内容。
在过去的 2 天里,我尝试想出一个解决方案,试图将我的头脑集中在递归上。我查看了此处与递归相关的帖子,但没有真正适合我的情况。
function getIncludes (original) {
let output = [];
function recursion (package) {
if (package.content) output.push(package.content.join(' '));
if (package.include) {
return package.include.forEach(str => {
let c = presets.find(obj => obj.name === str);
if (c.content) output.push(c.content.join(' '));
recursion(c)
});
}
}
recursion(original);
return output.join(' ');
}
示例预设对象
[
{
"id": 0,
"name": "videoFormats",
"content": ["(avi|mkv|mov|mp4|mpg|wmv)"],
"hidden": true,
"include": ["imageFormats"]
},
{
"name": "audioFormats",
"id": 1,
"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
}
]
我需要一个能给我一个预设列表的函数,所选的预设取决于。
这样的函数就可以了。
getIncludes("media") returning ["videoFormats", "audioFormats", "imageFormats"]
最佳答案
首先,我们需要考虑某种类型 T
,它允许通过 name
高效查找特定预设。数组不提供这样的功能,所以我们将从数组转换为我们想要的类型,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)
现在我们可以通过名称
轻松找到预设,我们编写一个通用的遍历程序。这使我们能够将 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"))
// []
关于javascript - 遍历相互引用的对象数组并将它们从字符串解析为对象,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56990521/