Javascript数组递归问题 - 遍历“部分”

Posted

技术标签:

【中文标题】Javascript数组递归问题 - 遍历“部分”【英文标题】:Javascript array recursion question - looping through "sections" 【发布时间】:2022-01-08 09:31:45 【问题描述】:

我正在努力使用 javascript 来查找具有 n 个深度的数组源的所有组合,该数组源被分成多个部分(以下示例中的 0、1 和 2)。我想以每种可能的组合结束 - 每个返回的数组应包含每个组中的一个且仅包含一个值。我已经将解决​​方案硬编码为 4 个级别,但需要更大的灵活性——递归提供的灵活性。我已经查看了lotsofpossiblerecursivesolutions,虽然我了解这些是如何工作的,但我只是不知道如何让这个特定的源数据工作。

sourceArr=[
     [0,60,100]
    ,[0,60,200]
    ,[0,66,300]
    ,[1,69,500]
    ,[2,70,600]
    ,[2,70,700]
    ,[2,77,800]
    ,[2,77,900]
]

预期的返回值...

[
    [60,100],69,500,70,600]
    ,[60,100],69,500,70,700]
    ,[60,100],69,500,77,800]
    ,[60,100],69,500,77,900]
    ,[60,200],69,500,70,600]
    ,[60,200],69,500,70,700]
    ,[60,200],69,500,77,800]
    ,[60,200],69,500,77,900]
    ,[66,300],69,500,70,600]
    ,[66,300],69,500,70,700]
    ,[66,300],69,500,77,800]
    ,[66,300],69,500,77,900]
]

【问题讨论】:

我不明白sourceArr和预期输出之间的逻辑关系 如果你也包含你的代码会很好,即使它不起作用。 为什么需要递归?你看过here或here吗? @NinaScholz:你不需要它,但递归是编写笛卡尔积函数的一种有用方法。另请注意,例如,第一个链接中的your generally useful answer 可能不起作用,因为它会造成不必要的扁平化。 【参考方案1】:

本质上,这是一个cartesian product 的问题。但是您必须先了解它,因为您没有要在单独的数组中分组的元素。因此,首先,您需要按数组的第一个元素对数组进行分组,然后去掉第一个元素。

如果我们使用一些简单的实用函数,我们可以编写一个简单的版本,如下所示:

const combine = pipe (
  group (head),
  map (map (tail)),
  cartesian
) 

在这里,我们pipe 将多个函数组合在一起,创建了一个新函数,该函数接受一些输入,将其发送到第一个函数,然后将那个输出发送到第二个,然后将输出发送到第三个,以此类推,返回最终输出。

我们在此管道中提供的第一个函数groups 根据应用于每个数组的head 函数的结果提供到数组中的元素(它只是返回数组的第一个元素。)这将给我们留下像这样的结构:

[
  [[0, 60, 100], [0, 60, 200], [0, 66, 300],
  [[1, 69, 500]],
  [[2, 70, 600], [2, 70, 700], [2, 77, 800], [2, 77, 900]]
]

接下来我们使用嵌套的map 调用,将tail 传递给最里面的调用。 tail 只返回数组的第一个元素之外的所有元素。这会将上面的内容转换为

[
  [[60, 100], [60, 200], [66, 300],
  [[69, 500]],
  [[70, 600], [70, 700], [77, 800], [77, 900]]
]

现在这是Cartesian product 函数使用的格式,所以我们包含一个简单的cartesian 函数,我们就完成了。

我们可以这样写这些助手:

// utility functions
const head = (xs) => xs [0]
const tail = (xs) => xs .slice (1)
const map = (fn) => (xs) =>
  xs .map (x => fn (x))
const pipe = (...fns) => (x) =>
  fns .reduce ((a, fn) => fn (a), x)
const group = (fn) => (xs) =>
  Object .values (xs .reduce (
    (a, x, _, __, k = fn (x)) => ((a[k] = [...(a[k] || []), x]), a), 
    
  ))
const cartesian = ([xs, ...xss]) =>
  xs == undefined
    ? [[]]
  : xs .flatMap (x => cartesian (xss) .map (ys => [x, ...ys]))

// main function
const combine = pipe (
  group (head),
  map (map (tail)),
  cartesian
) 

// sample data
const sourceArr = [[0, 60, 100], [0, 60, 200], [0, 66, 300], [1, 69, 500], [2, 70, 600], [2, 70, 700], [2, 77, 800], [2, 77, 900]]

// demo  -- stringify is to avoid SO's id-ref presentation
console .log (JSON.stringify(combine (sourceArr), null, 4))
.as-console-wrapper max-height: 100% !important; top: 0

请注意,为了做到这一点,我使用了我周围的函数。写这个答案比写代码花的时间要长得多。这就是维护一个可重用函数库的优势,您可以根据需要获取。

这些特定的 API 类似于 Ramda 的设计。这并不奇怪,因为我是 Ramda 的创始人和维护者,但它们很容易由我们自己创建和维护。

【讨论】:

非常感谢!我需要对其进行 JSON.parse 以将其恢复为数组,然后需要将每个子数组元素移动到单个元素中,但这很棒! 我忘了提到您的预期输出格式不正确,奇怪地混合了大括号样式。但是我的combine 的结果不是字符串。对 JSON.stringify 的调用仅用于在 *** 控制台中进行更清晰的报告。它实际上是一个数组,看起来像[[[60, 100], [69, 500], [70, 600]], [[60, 100], [69, 500], [70, 700]], [[60, 100], [69, 500], [77, 800]], ..., [[66, 300], [69, 500], [77, 900]]]

以上是关于Javascript数组递归问题 - 遍历“部分”的主要内容,如果未能解决你的问题,请参考以下文章

javascript遍历数组问题

递归遍历二叉树Javascript的所有嵌套子节点

JavaScript对象---递归遍历对象

Java数组去重复问题

在对象数组中查找递归(圆形)id javascript

23二叉搜索树的后序遍历序列