数组去重

又在不经意之间看到了一个面试题,应该算是比较久远的了

数组去重

为了培养 JavaScript 的语感,今天就把能想到的方法都梳理一遍

没有编程基础的人的思维

比如说现在有一个数组

var a = [1,2,2,3,3,4,4,5]

我猜按照正常的思维逻辑,肯定会这么想

  • a 中的第一项拿出来放到空数组 b
  • a 中的第二项拿出来和数组 b 的所有成员比一下,如果有重复的就跳过,如果没有重复的就把这一项加载到 b 里去
  • a 中的第三项拿出来和数组 b 的所有成员比一下,如果有重复的就跳过,如果没有重复的就把这一项加载到 b 里去
  • 。。。
  • 一直到最后一项

好了,接下来我们来将思路码出来

var a = [1,2,2,3,3,4,4,5]
var b = [] // 声明一个空数组

// 以下为伪代码
if(a[0] !== b[0]) { // 一开始 b 并没有成员,所以这条语句的意思就是把 a 的第一项添加到 b 上
    b.push(a[0])
}

if(a[1] !== b[0])  {
    b.push(a[1])
}

if(a[2] !== b[0] && a[2] !== b[1]){
    b.push(a[2])
}

....

能感受到一点规律么 if() {} 这一块可以用 for 循环搞定 而 if 后面的 () 里的内容可以用另一个 for 循环搞定

赶紧把想法写出来

var a = [1,2,2,3,3,4,4,5]
var b = [] // 声明一个空数组

for(var i = 0; i < a.length; i++) {
    var item = a[i]
    for(var j = 0; j < b.length; j++) {
        if(b[j] !== item) {
            // 天哪,写不下去了,我们并不能够用循环模拟出一个 && 的作用啊
        }
    }
}

代码的思路断了,不过这并不能难倒我,我们可以计数嘛

先初始化一个 k,只要 b[j] !== itemk 就加 1,最后统计一下每过一轮 for(var i...) 的循环后 k 的值就能得知到底有没有重复的了,好聪明

var a = [1,2,2,3,3,4,4,4,4,4,4,5]
var b = [] // 声明一个空数组

for(var i = 0; i < a.length; i++) {
    var item = a[i]
    var k = 0
    for(var j = 0; j < b.length; j++) {
        if(b[j] !== item) {
            k = k + 1
        }
    }
    if(k === b.length || i === 0) { // 加 || 运算符后面这句话是因为当最外层的循环 i = 0 的时候,内层的循环并不会执行
        b.push(item)
        
    }
}
console.log(b) // [1, 2, 3, 4, 5]

好了,这就是数组去重的方法,总结一下思路

  • 初始化一个空数组 b
  • 逐个将待处理数组 a 的成员与 b 数组中的每一个成员比较
  • 如果都不相等,才将这个 a 数组里的成员放到 b 数组

以上就是符合没有学过编程的人但想要解答这道题的基本思路,不知道有没有认同的

但是我们是程序员啊,不能就这么算了,看看能不能把这段代码再优化优化

我觉得计数太麻烦了,能不能不计数,想一想

这时候,我看到了上面总结的思路的最后一句话

如果都不相等,才将这个 a 数组里的成员放到 b 数组

换言之

只要有一个相等,数组 b 就不会被添加新成员

有了思路就马上实施

我们把

if(b[j] !== item) {...}

改成

if(b[j] === item) {...}

如果数组 b 的成员只要有一个和 item 相等,那么就不会 b.push(item)

var a = [1,2,2,3,3,4,4,4,4,4,4,5]
var b = [] // 声明一个空数组

for(var i = 0; i < a.length; i++) {
    var item = a[i]
    for(var j = 0; j < b.length; j++) {
        if(b[j] == item) {
            break; // 只要有相等的情况,就跳出这个 for 循环
        }
    }
    if(j === b.length || i === 0) { // 加 || 运算符后面这句话是因为当最外层的循环 i = 0 的时候,内层的循环并不会执行
        b.push(item)
    }
}

console.log(b) // [1, 2, 3, 4, 5]

是的,比一开始的代码稍微简洁那么一点。。。

先排个序呢

我们知道,怎么给数组排序

var a = [2,2,1,4,4,4,3,3,5]
a.sort(function(a, b) {
  return a - b
})
console.log(a) // [1, 2, 2, 3, 3, 4, 4, 4, 5]

对于一个还排序的数组去重的方法,我好像有了一个新思路

从数组的第二个成员开始,与其前一项做对比,如果相等,那么说明有重复,删除之

说干就干!

var a = [2,2,1,4,4,4,3,3,5]
a.sort(function(a, b) {
  return a - b
})
for(var i = 1; i < a.length; i++) {
    if(a[i] === a[i - 1]) {
        a.splice(i, 1)
    }
}
console.log(a) // [1, 2, 3, 4, 4, 5]

额,并没有成功,有两个重复的4

这一小段代码很好分析,因为 a.splice(i, 1) 删掉了一个成员,但是 for 循环里面的 i 还是沿用没有删掉成员的值,所以

var a = [2,2,1,4,4,4,3,3,5]
a.sort(function(a, b) {
  return a - b
})
for(var i = 1; i < a.length; i++) {
    if(a[i] === a[i - 1]) {
        a.splice(i, 1)
        i -= 1 // 因为删掉了一个成员,成员数就得减1
    }
}
console.log(a) // [1, 2, 3, 4, 5]

这个方法有个副作用,就是数组的顺序变了

你听过对象的键名有重复的么

是啊,对象的键名是没有重复的,这给了我新思路

var a = [1, 2, 2, 3, 3, 4, 4, 4, 5]
var b = []
var o = {}
for(var i = 0; i < a.length; i++) {
    var item = a[i]
    if(!o[item]) {
        o[item] = true
        b.push(item)
    }
}
console.log(b) // [1, 2, 3, 4, 5]

这个方法有个缺陷,他分不清 1"1",因为键名会默认转换为字符串

var a = [1, '1', 2, 2, 3, 3, 4, 4, 4, 5]
var b = []
var o = {}
for(var i = 0; i < a.length; i++) {
    var item = a[i]
    if(!o[item) {
        o[item] = true
        b.push(item)
    }
}
console.log(b) // [1, 2, 3, 4, 5] // 分不清 1 和 '1'

这也有解决的办法

  • 如果键名不存在,毫无疑问,这个值是没有重复的
  • 如果键名存在,那么利用 typeof 判断一下他的类型
  • 如果这个类型之前出现过,不管
  • 如果这个类型之前没出现过,就把它放到新数组

试试

var a = [1, '1', 2, 2, 3, 3, 4, 4, 4, 5]
var b = []
var o = {}
for(var i = 0; i < a.length; i++) {
    var item = a[i]
    var type = typeof a[i]
    if(!o[item]) {
        o[item] = [type]
        b.push(item)
    }else if(o[item].indexOf(type) === -1) {
        o[item].push(type)
        b.push(item)
    }
}
console.log(b) // [1, "1", 2, 3, 4, 5] // 现在分得清 1 和 '1' 了

当然了,这方法还是有缺陷,因为

console.log(typeof []) // Object
console.log(typeof {}) // Object

也就是说如果数组里的成员是简单类型的那么好办,对于复杂类型的就无能为力了

先进的 ES5

ES5 新增了 indexOf() 方法,接受两个参数

  • 要查找的项
  • 查找位置起点的索引

返回查找到的项的位置,如果没找到返回 -1

看完这段介绍我似乎又有思路了

var a = [1, 2, 2, 3, 3, 4, 4, 4, 5]
var b = []

for(var i = 0; i < a.length; i++) {
    if(b.indexOf(a[i]) === -1){
        b.push(a[i])
    }
}
console.log(b) //  [1, 2, 3, 4, 5]

搞定

懒惰的 ES6

ES6 提供了新的数据结构 Set

他类似于数组,但是成员的值是唯一的,比如

const set = new Set([1,2,2,3,3,4,4,4,5])
console.log(set) // Set(5) {1, 2, 3, 4, 5}

这就简单了,再把这个数据结构转换成数组就行了么

ES6 又提供了一个新的运算符 ...,他的其中一个功能就是吧任何类似数组的对象转为真正的数组

const set = new Set([1,2,2,3,3,4,4,4,5])
console.log([...set]) // [1, 2, 3, 4, 5]

好了

程序员的三大美德

Perl 语言的发明人 Larry Wall 说,好的程序员有3种美德:

  • 懒惰
    • 是这样一种品质,它使得你花大力气去避免消耗过多的精力。它敦促你写出节省体力的程序,同时别人也能利用它们。为此你会写出完善的文档,以免别人问你太多问题。
  • 急躁
    • 是这样一种愤怒----当你发现计算机懒洋洋地不给出结果。于是你写出更优秀的代码,能尽快真正的解决问题。至少看上去是这样。
  • 傲慢
    • 极度的自信,使你有信心写出(或维护)别人挑不出毛病的程序。

这篇文章就展现了程序员的其中一个美德

懒惰

(完)


文档信息

  • 自由转载-非商用-非衍生-保持署名
  • 发表日期:2017年5月17日
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,736评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,167评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,442评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,902评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,302评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,573评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,847评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,562评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,260评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,531评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,021评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,367评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,016评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,068评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,827评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,610评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,514评论 2 269

推荐阅读更多精彩内容