系列文章
- [ CodeWar ] - 001:过滤重复字符
- [ CodeWar ] - 002:最大和最小值
- [ CodeWar ] - 003:判断质数
- [ CodeWar ] - 004:处理数组元素
- [ CodeWar ] - 005:用户分组
- [ CodeWar ] - 006:数组比对
- [ CodeWar ] - 007:找不同
题目
这是一个系列题目,我们先来看看第一题:

需求:
- 传入的数组中除了一项以外,别的值都相等
- 找到这个与其他不同的值
解析
这是这个系列的第一题,思路非常简单:
- 将数组排序,那么特殊的值要么在首,要么在尾
- 如果
arr[0] === arr[1]则返回arr.pop() - 否则返回
arr.[0]即可
按照上面的思路实现:
const findUniq = (arr) =>
arr.sort()[0] === arr.sort()[1]
? arr.sort()[arr.sort().pop()]
: arr.sort()[0];
进阶
上面的思路已经非常简单,没必要再优化,所以我们接下来看看下一题:

相比于第一题,这里仅仅是将数组的元素换成了字符串,但实际上难度缺是实实在在的提升了:至少这里没法通过排序的方式来取巧了。
思路
我们来梳理一下题目中蕴含的条件:
- (原题下方提示了,数组长度最少为 3,所以这里就不考虑数组长度 < 3 的情况)
- 数组元素为字符串,大小写不确定,长度不确定,存在重复字符串
- 需要找出去重之后,大写/小写之后与别的字符串不同的一项
条件其实不多,并且每个元素也相对比较复杂,我们这里来捋一捋思路:
- 对于每个元素,我们可以将其转化成小写,去重,这样就是我们将要用来比较的最终元素了
- 而对于乱序字符串的比较,我们可以转化成数组,利用
sort()排序,再转回字符串进行比较
结合上述两点,思路就比较清晰了:
function findUniq(arr) {
let curStr, nxtStr;
for (let index = 0; index < arr.length - 1; index++) {
curStr = [...new Set(arr[index].toLocaleLowerCase().split(""))]
.sort()
.join("");
nxtStr = [...new Set(arr[index + 1].toLocaleLowerCase().split(""))]
.sort()
.join("");
if (curStr !== nxtStr) {
return arr[index + 1];
}
}
}
上面的代码看起来似乎没什么问题,但在实际测试中发现,我们少考虑了一个情况:
- 如果是数组的第一项与之后的项不同,这里返回的是数组的第二项
知道问题在哪儿,修复也就很简单了,添加一个条件判断即可:
function findUniq(arr) {
let firStr, secStr, thrStr, curStr, nxtStr;
firStr = [...new Set(arr[0].toLocaleLowerCase().split(""))].sort().join("");
secStr = [...new Set(arr[1].toLocaleLowerCase().split(""))].sort().join("");
thrStr = [...new Set(arr[2].toLocaleLowerCase().split(""))].sort().join("");
if (firStr !== secStr && firStr !== thrStr) {
return arr[0];
}
for (let index = 0; index < arr.length - 1; index++) {
curStr = [...new Set(arr[index].toLocaleLowerCase().split(""))]
.sort()
.join("");
nxtStr = [...new Set(arr[index + 1].toLocaleLowerCase().split(""))]
.sort()
.join("");
if (curStr !== nxtStr) {
return arr[index + 1];
}
}
}
优化
当然,上面的代码虽然实现了功能,但是逻辑是比较复杂的,那么这里我们从什么角度进行优化呢?
最简单的,首先我们可以把对比功能抽离出来,逻辑就会清晰很多:
function similar(cur, nxt) {
cur = [...new Set(cur.toLocaleLowerCase().split(""))].sort().join("");
nxt = [...new Set(nxt.toLocaleLowerCase().split(""))].sort().join("");
return cur !== nxt ? false : true;
}
function findUniq(arr) {
let [fir, sec, thr] = arr.slice(0, 3);
if (!similar(fir, sec) && !similar(fir, thr)) return fir;
for (const cur of arr) if (!similar(fir, cur)) return cur;
}
这里我们再来尝试一下别的思路:
- 由于我们的目的是找到数组中的某个元素,所以在返回的时候,直接返回
arr[x]即可 - 如上,现在我们关注的点是
x的值 - 首先我们可以通过
map()将数组中的值转化成方便比对的形式 - 然后我们可以借助
findIndex()来找到这个x - 经过前面的分析我们已经知道(这里说的前面是包括前面的实现),数组经过转化之后,里面的元素已经不区分大小写,顺序相同,并且没有重复,形成了类似
["a", "a", "b", "a"...]的形式 - 那么我们只要找到某个元素,它在数组中第一次和最后一次在数组中出现的位置相同,那么它一定是唯一的
综合上述思路,我们的代码就比较好实现了:
const findUniq = (arr) =>
arr[
arr
.map((x) => [...new Set([...x.toLowerCase()].sort())].join(""))
.findIndex((x, i, ar) => ar.indexOf(x) === ar.lastIndexOf(x))
];
当然,同样的思路,还能写的更骚一些:
findUniq = (
a,
b = a.map((a) => [[...new Set(a.toLowerCase())].sort().join``, a]),
c = b.map((a) => a[0])
) => b[c.findIndex((a) => c.indexOf(a) == c.lastIndexOf(a))][1];
进阶

其实在实现了