首页 > 解决方案 > 如何通过递归将重复的字符串/数字推送到 newArr?

问题描述

如何使用递归检查数组中的重复数字/字符串,然后将它们推送到 newArr 中,并将唯一的数字/字符串推送到 newArrTwo

function rec(those) {
 let [unique, twin] = [ [], [] ];
 
};

console.log(rec([2, "str", "arr", "str", 2, "obj", "arg"]));
console.log(rec(["ref", "val", "val", "val", "el", "el"]));

我想要的输出是unique = ["arr", "obj", "arg"];twin = [2,"str"]

标签: javascriptarraysloopsrecursion

解决方案


更新

我最初误读了规格。在本节下面,我详细介绍了针对不同但相关问题的几种解决方案。这是请求的解决方案:

const rec = (arr, uniqs = new Set, dups = new Set, [x, ...xs] = arr) => 
  arr.length == 0
  ? [[...uniqs], [...dups]]
  : uniqs.has(x) || dups.has(x)
    ? rec(xs, (uniqs.delete(x), uniqs), dups.add(x))
    : rec(xs, uniqs.add(x), dups)

console.log(rec([2, "str", "arr", "str", 2, "obj", "arg"]));
console.log(rec(["ref", "val", "val", "val", "el", "el"]));

请注意,最终原始答案有两个变化:

  • 我们也切换到了 Set dups,并进行了所需的更改。

  • 此行现在从uniqsSet 中删除,而不仅仅是传递它:

    ? rec(xs, (uniqs.delete(x), uniqs), dups.add(x))
    

这实际上指向了一个 API 问题Setadd返回集合真的很有用。delete不这样做也太糟糕了。虽然布尔返回有时可能会有所帮助,但使用has. 解决这个问题为时已晚,但这确实是一种耻辱。

原始答案

这是一种可能的方法。ES6 的特性,例如休息参数和默认参数,以及Symbol一个相当优雅的实现。

const None = Symbol()

const rec = ([x = None, ...xs], uniqs = [], dups = []) => 
  x == None 
  ? [uniqs, dups]
  : uniqs.includes(x)
    ? rec(xs, uniqs, dups.concat(x))
    : rec(xs, uniqs.concat(x), dups)

console.log(rec([2, "str", "arr", "str", 2, "obj", "arg"]));
console.log(rec(["ref", "val", "val", "val", "el", "el"]));

现代 JS 中递归的一个真正帮助是能够使用默认参数来避免额外的辅助函数。

使用Symbolhere 是一种有趣的方式,可以将输入数组的展开组合成一个头部和一个尾部,并且仍然允许我们测试它是否为空。另一种方法如下所示。

const rec = (arr, uniqs = [], dups = [], [x, ...xs] = arr) => 
  arr.length == 0 
  ? [uniqs, dups]
  : uniqs.includes(x)
    ? rec(xs, uniqs, dups.concat(x))
    : rec(xs, uniqs.concat(x), dups)

console.log(rec([2, "str", "arr", "str", 2, "obj", "arg"]));
console.log(rec(["ref", "val", "val", "val", "el", "el"]));

这个版本感觉比原来的干净一些,但它不需要定义一个辅助符号来检查是否有空。

这仍然有问题;它可能有性能问题。因为我们必须O(n) includes为每个元素调用 ,所以整个解决方案类似于O(n^2),取决于唯一值与重复值的比率。这可能不是问题,我只会在以下两种情况之一中修复它:

  • 我已经测试并发现此代码是我的应用程序中的实际瓶颈
  • 我有一个性能更高的替代版本,它几乎不牺牲代码清晰度。

在这种情况下,我确实有这样一个版本:

const rec = (arr, uniqs = new Set(), dups = [], [x, ...xs] = arr) => 
  arr.length == 0
  ? [[...uniqs], dups]
  : uniqs.has(x)
    ? rec(xs, uniqs, dups.concat(x))
    : rec(xs, uniqs.add(x), dups)

console.log(rec([2, "str", "arr", "str", 2, "obj", "arg"]));
console.log(rec(["ref", "val", "val", "val", "el", "el"]));

(我们也可以Symbol在这个版本中使用替代方案。)

在这里,我们切换到使用 a Set,这正是为处理独特元素集合而设计的数据类型。这需要对集合进行一些解包,[...uniqs]或者Array.from(uniqs)- 但这只是复杂性的非常小的增加。其他更改只是与从数组切换到集合有关。


推荐阅读