數組去重

又在不經意之間看到了一個面試題,應該算是比較久遠的了

數組去重

為了培養 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閱讀 230,247評論 6 543
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,520評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,362評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,805評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,541評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,896評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,887評論 3 447
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,062評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,608評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,356評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,555評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,077評論 5 364
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,769評論 3 349
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,175評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,489評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,289評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,516評論 2 379

推薦閱讀更多精彩內容