我想测试一个数组只包含唯一元素,我的解决方案如下:
function uniqueElements(a) {
var r = true;
while (a) {
var [el, a] = [a.slice(0,1), a.slice(1)];
r &= !a.includes(el);
};
return !!r;
}
此方法有效。但是,由于我采用了更具功能性的风格,并且因为折叠非常棒,我想实现一个看起来像这样的函数:
function uniqueElements(a) {
var isUnique = (acc, el) => acc &= !remainingArray.includes(el);
return a.reduce(isUnique, true);
}
我无法弄清楚如何到达remainingArray
变量。有人知道怎么做吗?这在JS中是否可行,如果不是,那么该功能如何通过折叠来表达?
答案 0 :(得分:2)
记住不要陷入思维模式。折叠很棒,但是在JavaScript中,如果我们的结果可以在遍历整个数组之前计算,则无法提前停止折叠
换句话说,以下是什么答案? true
或false
?
uniqueElements ( [ 1 , 1 , 2 , 3 , ... thousands more items ] )
// => true or false ?
我们可以在处理完第二个false
后立即确定答案1
。我们无需在2
或3
或其他数组中保持折叠,因为它们不会影响false
结果
可能的解决方案是一个简单的递归过程
const isUnique = ([ x, ... xs ], set = new Set ()) =>
x === undefined
? true
: set.has (x)
? false // we already found a non-unique, stop recurring
: isUnique (xs, set.add (x))
console.log (isUnique ([]))
// true
console.log (isUnique ([ 1, 2, 3 ]))
// true
console.log (isUnique ([ 1, 1, 2, 3 ]))
// false

或者仍然保持纯粹功能界面的堆栈安全解决方案 - 如果我不得不猜测,这可能比上述程序快10倍并且不会暴露私有API
const isUnique = xs =>
{
const set = new Set ()
for (const x of xs)
if (set.has (x))
return false
else
set.add (x)
return true
}
console.log (isUnique ([]))
// true
console.log (isUnique ([ 1, 2, 3 ]))
// true
console.log (isUnique ([ 1, 1, 2, 3 ]))
// false

或者制定自己的解决方案 - 无论哪种方式,都不要陷入困境,认为只要您触摸可遍历的数据结构,就需要使用折叠。
从更一般的意义上说,你需要练习想象你的函数的过程是什么样的。我建议您在使用铅笔和纸张时使用编译器/评估器,而您首先要掌握它。最终,您将能够设想出简单的过程;然后是随着时间的推移练习更复杂的 - 我说这是因为如果你能看到在之后继续折叠看起来多么愚蠢,你可能无法完成这个任务。结果可以返回
就此而言,这就是为什么我使用Set
来检查唯一身份而不是.includes
。集合可以进行二进制搜索,而数组搜索是线性的 - 一旦你可以看到对于一个非常大的输入看起来是什么样的过程,一个一个地查找你的项目就像是傻了。只有在您构想过程时,您才能看到像Set这样的替代数据结构如何能够显着降低函数的时间复杂度
答案 1 :(得分:0)
您可以对数组进行切片:
function isUnique(xs) {
return xs.reduce((acc, x, i) => {
if (xs.slice(i + 1).includes(x)) {
return false;
}
return acc;
}, true);
}
尽管如评论中所述,如果数组中有字符串或数字,也可以使用哈希值来获得更好的性能。