找到所有路径组合。

huangapple go评论76阅读模式
英文:

Find all path combinations

问题

不知道这是否有一个具体的名称,但我需要将一个由一维数组和简单元素组成的数组展平,以便报告直到叶子“节点”的所有组合。这是一个示例,因为上面的描述可能会让人感到很难理解:

// 我的输入数组包含单个元素或一维数组:
let input = [1, 2, [3, 4], [5, 6]];

展开的过程是这样的,每当遇到一个数组时,路径会分成与数组包含的元素数量相同的部分:

// 当前结果 = [1, 2]
// 未使用的输入 [[3, 4], [5, 6]]
// ->
// 当前结果 = [ [1, 2, 3], [1, 2, 4] ]

// 当前结果 = [ [1, 2, 3], [1, 2, 4] ]
// 未使用的输入 [[5, 6]]
// ->
// 最终结果 = [ [1, 2, 3, 5], [1, 2, 4, 5], [1, 2, 3, 6], [1, 2, 4, 6] ]

我可能在复制和别名方面搞错了些什么,但似乎无法让它正确工作并处理特殊情况,例如:

let input1 = [1, 2, 3]; // 没有嵌套数组
let input2 = [];        // 空输入

尝试反向构建结果,因为.pop 在这里很方便,但无法让它正常工作:


function flatPath(input, result = [[]]) {
    while (input.length) {
        const last = input.pop();

        if (Array.isArray(last)) {
            result = flatPath(last, [...result, ...result]);
        } else {
            for (let ar of result) {
                result.push(last);
            }
        }
    }
    return result;
}

let result = flatPath([1, 2, [3, 4], [2, 5, 6] ]);

console.log(result);

但甚至无法通过编译(我正在使用 TypeScript),因为我得到的只是:

参数 'input' 隐式具有 'any' 类型。

类型为 'any' 的参数不能赋给类型为 'never' 的参数。

我的代码有什么问题,或者是否有更好(更符合习惯的)的方法来做到这一点。

英文:

Don't know if this has a specific name, but I need to flatten an array of 1D arrays and simple elements, in a way that all combinations untill a leaf "node" are reported. Here's an example because the above leaves a lot to the imagination:

// My input array contains single elements or 1D arrays:
let input = [1, 2, [3, 4], [5, 6]];

The unfolding proceeds so that each time an array is encountered, the paths are split into as many elements as the array contains:

// current result = [1, 2]
// unconsumed input [[3, 4], [5, 6]]
// ->
// current result = [ [1, 2, 3], [1, 2, 4] ]

// current result = [ [1, 2, 3], [1, 2, 4] ]
// unconsumed input [[5, 6]]
// ->
// final result = [ [1, 2, 3, 5], [1, 2, 4, 5], [1, 2, 3, 6], [1, 2, 4, 6] ]

I may be messing something up with copies and aliasing but can't seem to make it work right and handle special cases like:

let input1 = [1, 2, 3]; // No nested arrays
let input2 = [];        // Empty input

Tried building the result backwards since .pop is convenient to use here but can't get it to work:


function flatPath(input, result = [[]]) {
    while (input.length) {
        const last = input.pop();

        if (Array.isArray(last)) {
            result = flatPath(last, [...result, ...result]);
        } else {
            for (let ar of result) {
                result.push(last);
            }
        }
    }
    return result;
}

let result = flatPath([1, 2, [3, 4], [2, 5, 6] ]);

console.log(result);

but can't even get past compilation (I'm using typescript) since all I get is:

> Parameter 'input' implicitly has an 'any' type.

> Argument of type 'any' is not assignable to parameter of type 'never'.

What is the problem with my code or is there a better (more idiomatic) way to do this.

答案1

得分: 2

这是一个很好的生成器函数的用例,因为它允许您在叶子节点(其基数是未知的先验)处yield结果,而不必处理递归的调用树向上聚合。

const flatPath = function*(array, prefix = []) {
  const next = array[0];
  if (next === undefined) { // 基本情况
    yield prefix;
  } else if (next instanceof Array) {
    for (let item of next) {
      yield * flatPath(array.slice(1), [...prefix, item]);
    }
  } else {
    yield * flatPath(array.slice(1), [...prefix, next]);
  }
};

const result = Array.from(flatPath([1, 2, [3, 4], [2, 5, 6]]));
console.log(result);

/* 输出:
[
  [ 1, 2, 3, 2 ],
  [ 1, 2, 3, 5 ],
  [ 1, 2, 3, 6 ],
  [ 1, 2, 4, 2 ],
  [ 1, 2, 4, 5 ],
  [ 1, 2, 4, 6 ]
]
*/

如上所示,这是您提供的代码的翻译。

英文:

This is a great use-case for a generator function, because it allows you to yield results at the leave notes (whose cardinality is unknown a-priori), rather having to handle the aggregation back up the calling tree of the recursion.

<!-- begin snippet: js hide: false console: true babel: false -->

<!-- language: lang-js -->

const flatPath = function*(array, prefix = []) {
  const next = array[0];
  if (next === undefined) { // base case
    yield prefix;
  } else if (next instanceof Array) {
    for (let item of next) {
      yield * flatPath(array.slice(1), [...prefix, item]);
    }
  } else {
    yield * flatPath(array.slice(1), [...prefix, next]);
  }
};

const result = Array.from(flatPath([1, 2, [3, 4], [2, 5, 6]]));
console.log(result);

/* output:
[
  [ 1, 2, 3, 2 ],
  [ 1, 2, 3, 5 ],
  [ 1, 2, 3, 6 ],
  [ 1, 2, 4, 2 ],
  [ 1, 2, 4, 5 ],
  [ 1, 2, 4, 6 ]
]
*/

<!-- end snippet -->

答案2

得分: 2

你可以在这里从底部向上聚合结果,而不需要传递上下文数组,使用 map() 将前一次调用的每个非数组元素添加到深层调用返回的子数组中。

function flatPath([next, ...rest]) {
  if (next === undefined) {
    return [[]];
  }

  if (Array.isArray(next)) {
    const temp = [];
    for (const n of next) {
      temp.push(...flatPath([n, ...rest]));
    }
    return temp;
  }

  return flatPath(rest).map(t => [next, ...[].concat(t)]);
}

// Test output
for (const test of [
  [1, 2, [3, 4], [5, 6]],
  [1, 2, 3], 
  [[1, 2, 3], 4], 
  []
]) {
  let result = flatPath(test);
  console.log(JSON.stringify(result));
}

上述代码会在你的数组中有 undefined 元素时失败,下面的修复方法是通过检查 ...rest 的长度来确定底部(以及明确检查传入的空数组)。

function flatPath(input) {
  if (input.length === 0) {
    return [];
  }

  const [next, ...rest] = input;

  if (Array.isArray(next)) {
    const temp = [];
    for (const n of next) {
      const tail = rest.length
        ? flatPath([n, ...rest])
        : flatPath([].concat(n));

      temp.push(...tail);
    }
    return temp;
  }

  return rest.length
    ? flatPath(rest).map(t => [next, ...[].concat(t)])
    : [next];
}

// Test output
for (const test of [
  [1, 2, [3, 4], [5, 6]],
  [1, 2, 3], 
  [[1, 2, 3], 4], 
  []
]) {
  let result = flatPath(test);
  console.log(JSON.stringify(result));
}

希望这对你有帮助。

英文:

You can handle this without passing down a context array by aggregating the results from the bottom up here using map() to add each non-array element from the previous call to the returned sub-arrays from deeper calls.

<!-- begin snippet: js hide: false console: true babel: false -->

<!-- language: lang-js -->

function flatPath([next, ...rest]) {
  if (next === undefined) {
    return [[]];
  }

  if (Array.isArray(next)) {
    const temp = [];
    for (const n of next) {
      temp.push(...flatPath([n, ...rest]));
    }
    return temp;
  }

  return flatPath(rest).map(t =&gt; [next, ...[].concat(t)]);
}


// Test output
for (const test of [
  [1, 2, [3, 4], [5, 6]],
  [1, 2, 3], 
  [[1, 2, 3], 4], 
  []
]) {
  let result = flatPath(test);
  console.log(JSON.stringify(result));
}

 // [[1,2,3,5],[1,2,3,6],[1,2,4,5],[1,2,4,6]]
 // [[1,2,3]]
 // [[1,4],[2,4],[3,4]]
 // [[]]

<!-- end snippet -->

The above will fail if you have an undefined element in your array which is fixed below by checking against the ...rest length to determine the bottom (and an explicit check for empty arrays passed as input).

<!-- begin snippet: js hide: false console: true babel: false -->

<!-- language: lang-js -->

function flatPath(input) {
  if (input.length === 0) {
    return [];
  }

  const [next, ...rest] = input;

  if (Array.isArray(next)) {
    const temp = [];
    for (const n of next) {
      const tail = rest.length
        ? flatPath([n, ...rest])
        : flatPath([].concat(n));

      temp.push(...tail);
    }
    return temp;
  }

  return rest.length
    ? flatPath(rest).map(t =&gt; [next, ...[].concat(t)])
    : [next];
}

// Test output
for (const test of [
  [1, 2, [3, 4], [5, 6]],
  [1, 2, 3], 
  [[1, 2, 3], 4], 
  []
]) {
  let result = flatPath(test);
  console.log(JSON.stringify(result));
}

 // [[1,2,3,5],[1,2,3,6],[1,2,4,5],[1,2,4,6]]
 // [[1,2,3]]
 // [[1,4],[2,4],[3,4]]
 // [[]]

<!-- end snippet -->

答案3

得分: 1

我能编译您的代码,但它进入了无限循环。我认为问题出在这部分:

for (let ar of result) {
    result.push(last);
}

您正在在迭代result数组时向其添加元素。这会导致无限循环。

要解决此问题,您可以创建一个单独的数组来存储新元素,然后在循环之外将其与result数组连接起来。

以下的代码可以正常工作:

const newElements = [];
for (let ar of result) {
    newElements.push(last);
}
result = result.concat(newElements);
英文:

I am able to compile your code but it goes into infinite loop. I think the problem is this part;

        for (let ar of result) {
            result.push(last);
        }

You are pushing elements to the result array while iterating over it. This causes an infinite loop.

To fix this issue, you can create a separate array to store the new elements and then concatenate it with the result array outside the loop.

The following works fine;

        const newElements = [];
        for (let ar of result) {
            newElements.push(last);
        }
        result = result.concat(newElements);

答案4

得分: 1

这个版本是独立编写的,表达的算法与pilchard的答案相同。但它进行了一些整理,使用了纯表达式而不是语句,因为我总是更喜欢:

const process = ([xs, ...xss], rs = xs !== undefined && process(xss)) =>
  xs === undefined
    ? [[]]
  : Array.isArray(xs)
    ? xs.flatMap(x => rs.map(r => [x, ...r]))
    : rs.map(r => [xs, ...r]);

const input = [1, 2, [3, 4], [5, 6]]
console.log(JSON.stringify(process(input)))

Pilchard的答案还表达了对输入中可能的undefined值的担忧。如果这是一个问题,一个简单的解决方法是引入一个Symbol,就像这样:

const None = Symbol()

const process = ([xs = None, ...xss], rs = xs !== None && process(xss)) =>
  xs === None
    ? [[]]
  : Array.isArray(xs)
    ? xs.flatMap(x => rs.map(r => [x, ...r]))
    : rs.map(r => [xs, ...r])

const input = [1, 2, [3, 4], [5, 6]]

但除非你预期并且希望处理undefined值,否则我不会费心去做这个。

英文:

This version, written independently, expresses the same algorithm as the answer from pilchard. But it cleans some things up and uses pure expressions rather than statements, as I always prefer:

<!-- begin snippet: js hide: false console: true babel: false -->

<!-- language: lang-js -->

const process = ([xs, ...xss], rs = xs !== undefined &amp;&amp; process(xss)) =&gt;
  xs === undefined
    ? [[]]
  : Array.isArray(xs)
    ? xs.flatMap(x =&gt; rs.map(r =&gt; [x, ...r]))
  : rs.map(r =&gt; [xs, ...r]);

const input = [1, 2, [3, 4], [5, 6]]

console .log(JSON.stringify(process (input)))

<!-- end snippet -->

Pilchard's answer also expressed concern over possibly undefined values in the input. If that is a concern, an easy solution would be to introduce a Symbol, like this:

const None = Symbol()

const process = ([xs = None, ...xss], rs = xs !== None &amp;&amp; process(xss)) =&gt;
  xs === None
    ? [[]]
  : Array.isArray(xs)
    ? xs.flatMap(x =&gt; rs.map(r =&gt; [x, ...r]))
  : rs.map(r =&gt; [xs, ...r])

const input = [1, 2, [3, 4], [5, 6]]

But I wouldn't bother unless you expect -- and want to handle -- undefined values.

huangapple
  • 本文由 发表于 2023年7月7日 06:50:38
  • 转载请务必保留本文链接:https://go.coder-hub.com/76632961.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定