又在不經意之間看到了一個面試題,應該算是比較久遠的了
數組去重
為了培養 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] !== item
,k
就加 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日