javascript - 遍历相互引用的对象数组并将它们从字符串解析为对象

标签 javascript arrays object recursion tree

我正在创建一个预设系统,其中预设可以“包含”其他预设。

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/

相关文章:

javascript - 在javascript中更改对象的键和附加值的最佳方法?

php - 访问数组中的对象

object - 如何在不进行任何缩放的情况下显示 SVG

javascript - 转换对象数组

java - 主方法中无法访问二维数组值

c - 如何编辑指针数组的索引?

javascript - 将新函数插入数组 - Javascript

javascript - 在 SlideUp 动画结束之前再次触发函数时,动画会出现故障

javascript - 有没有可以附加数据的 Javascript 图表库?

javascript - 我有一个样式化的 td 类,它使我的评论按钮看起来像一个提交