摘要:缺點不兼容以下瀏覽器七高階函數方法用來判斷一個數組是否包含一個指定的值,根據情況,如果包含則返回,否則返回。方法六高階函數優點高階函數的高級用法。
一、前言
數組去重是一個老生常談的問題,但是有時候會彈出點其他東西。
二、雙重循環這個方法是最常見的,最原始的方法。
// 方法一:雙重循環 var array = [1,1,"1","2","1",1,2] function unique(arr){ // res 存結果 var res = []; for(var i = 0, length = arr.length; i < length; i++){ for(var j = 0, length2 = res.length; j < length2; j++){ if(arr[i] === res[j]){ break; } } if(j == length2){ res.push(arr[i]) } } return res; } unique(array); //[1, "1", "2", 2]
思路:雙層循環方法,使用的是循環嵌套,外層是arr,里層是res,如果arr[i]的值等于res[j]的值,則跳出當前循環,如果都不等于,說明元素唯一,這時候j的值等于res的長度,根據這個判斷,將值添加res中。
優點:兼容性好
缺點:時間復雜度o(n2)
三、indexOf方法思路:使用indexOf簡化內層循環。
// 方法二:indexOf簡化內層循環 var array = [1,1,"1","2","1",1,2] function unique(arr){ // res 存結果 var res = []; for(var i = 0, length = arr.length; i < length; i++){ var current = arr[i]; if(res.indexOf(current) === -1){ res.push(current); } } return res; } unique(array); //[1, "1", "2", 2]
優點:時間復雜度降低
缺點:有的瀏覽器不支持indexOf方法,時間復雜度o(n2)
四、排序后去重思路:使用快排sort將數組排序,這樣相同的值會被排在一起,只需要判斷當前元素與上一個元素是否相同,相同說明重復,不同就添加進res中。
// 方法三:排序后去重 var array = [1,1,"1","2","1",1,2] function unique(arr){ // res 存結果 var res = []; var sortedArray = arr.concat().sort(); console.log(sortedArray, "-=-=-=-=-=-=") var current; for(var i = 0, length = sortedArray.length; i < length; i++){ // 如果是第一個元素 或者是相鄰元素不相同 if(!i || current !== sortedArray[i]){ res.push(sortedArray[i]); } current = sortedArray[i]; } return res; } unique(array); //[1, "1", 1, "2", 2]
優點:已經排好序的數組去重,這種方法效率高于使用 indexOf,時間復雜度o(n)
缺點:已經修改數組的順序,同時存在去重不徹底
注意:sort函數默認排序是 Unicode,srot方法默認可是能給字母實現排序。突然發現了sort在排序的時候存在一個隱式轉換,會把要排序的對象轉換成字符串,sort排序的時候1和"1"是相同的,然后根據unicode比較大小,所以出現了[1, "1", 1, "2", 2]這種情況。
注意:數組進行了 array.concat()操作之后,其實是復制出來一份原有的數組,復制出來的新數組不會影響到原有數組。
五、使用ES5的filter思路:使用filter簡化外層循環
1、使用indexOf簡化內層循環// 方法四:filter + indexOf var array = [1,1,"1","2","1",1,2] function unique(arr){ // res 存結果 var res = arr.filter(function(item, index, arr){ return arr.indexOf(item) === index; }) return res; } unique(array); //[1, "1", "2", 2]2、排序去重的方法
// 方法四:filter + sort var array = [1,1,"1","2","1",1,2] function unique(arr){ // res 存結果 var res = arr.concat().sort().filter(function(item, index, arr){ return !index || item !==arr[index -1] }) return res; } unique(array); //[1, "1", 1, "2", 2]
上面已經講到了sort排序時候存在一個隱式轉換。
優點:很簡潔,思維也比較巧妙,直觀易懂,使用filter簡化外層循環
缺點:不支持 IE9 以下的瀏覽器,時間復雜度o(n*2)
六、Object鍵值對的問題思路:利用一個空的Object對象,把數組的值存成Object的key,比如就是Object[value] = true;循環判斷的時候,如果Object[value]存在,說明這個值重復了。
// 方法五:Object鍵值對 var array = [1,1,"1","2","1",1,2] function unique(arr){ // obj 存對象 var obj= {}; var res = arr.filter(function(item, index, arr){ if(obj.hasOwnProperty(item)) return false; else { return obj[item] = true; } }) return res; } unique(array); //[1, "2"]
然后我們發現1和"1"是不同的,但是這種方法會判斷為是同一個值,因為鍵值對中只能是字符串,會進行一個隱式轉換。和sort排序時候的轉換是一樣的,可以通過typeof item+ item,拼成字符串作為key的值避免這個問題
// 優化 function unique(arr){ // obj 存對象 var obj= {}; var res = arr.filter(function(item, index, arr){ if(obj.hasOwnProperty(typeof item + item)) return false; else { return obj[typeof item + item] = true; } }) return res; } unique(array); //[1, "1", "2", 2]
優點:hasOwnProperty是對象的屬性存在性檢查方法。對象的屬性可以基于hash表實現,因此對屬性的方法的時間復雜度達到O(1);filter是數組迭代的方法,內部是一個for循環,復雜度O(n)。總的時間復雜度O(n)。
缺點:不兼容IE9以下瀏覽器
七、reduce高階函數includes() 方法用來判斷一個數組是否包含一個指定的值,根據情況,如果包含則返回 true,否則返回false。
// 方法六:reduce高階函數 var array = [1,1,"1","2","1",1,2] function unique(arr){ let newArr = arr.reduce((pre,cur)=>{ if(!pre.includes(cur)){ return pre.concat(cur) }else{ return pre } },[]); return newArr; } console.log(unique(array));// [1, "1", "2", 2]
優點:高階函數的高級用法。
缺點:兼容性問題,對象數組不能使用includes方法來檢測。includes區分大小寫
八、ES6的Set數據結構只能說ES6標準越來越好,可以使用Set數據結構,ES6中提供了set數據結構,類似于數組,成員值都是唯一的,沒有重復的。
// 方法七:ES6的set var array = [1,1,"1","2","1",1,2] function unique(arr){ return Array.from(new Set(arr)); } unique(array); //[1, "1", "2", 2]
還可以用擴展運算符...
// 優化 var array = [1,1,"1","2","1",1,2] function unique(arr){ return [...new Set(arr)]; } unique(array); //[1, "1", "2", 2]
再寫簡單點
// 再優化 var array = [1,1,"1","2","1",1,2] const unique = arr => [...new Set(arr)]; unique(array); //[1, "1", "2", 2]
優點:ES6語法簡單高效。
缺點:兼容性問題,加上使用babel編譯不是問題。
九、ES6的Map數據結構// 方法八:ES6的Map + filter var array = [1,1,"1","2","1",1,2]; function unique(arr){ var current = new Map(); var res = arr.filter(function(item, index, arr){ if(current.has(item)){ return false; }else{ return current.set(item, 1); } }) return res; } unique(array); //[1, "1", "2", 2]
思路:使用map的方法set和has,用has方法來判斷是否存在這個key,如果沒有值將map中存一個key-value。
注意:最新的ES6規范引入了新的數據類型Map,為了解決對象中的鍵只能是字符串的問題,其實其他基本數據類型是可以作為鍵的。
優點:ES6語法簡單高效。
缺點:兼容性問題,加上使用babel編譯不是問題。
十、特殊數據類型比較var str1 = "1"; var str2 = new String("1"); console.log(str1 == str2); // true console.log(str1 === str2); // false console.log(null == null); // true console.log(null === null); // true console.log(undefined == undefined); // true console.log(undefined === undefined); // true console.log(NaN == NaN); // false console.log(NaN === NaN); // false console.log(/a/ == /a/); // false console.log(/a/ === /a/); // false console.log({} == {}); // false console.log({} === {}); // false
看個栗子1
var arr = [1, 2, NaN]; arr.indexOf(NaN); // -1
原因:indexOf底層還是使用 === 來進行判斷的,因為NAN === NAN結果是false,使用indexOf還是查不到NAN這個元素。
再看個栗子2
function unique(array) { return Array.from(new Set(array)); } console.log(unique([NaN, NaN])) // [NaN]
Set中,NAN === NAN是false,但是這兩個元素是重復的
十一、后話在對數組去重的性能進行優化,然后想了半天也只寫出來了Object鍵值對的性能,找不到其他既能判斷引用類型,性能又穩定在n^2之內的方式?
自己回答一下:
目前時間復雜度到O(n)的方法:
(1)Object鍵值對,實質是hasOwnProperty的hash表。
(2)ES6的set,map的數據結構
(3)reduce高階函數
【注:我是saucxs,也叫songEagle,松寶寫代碼,文章首發于sau交流學習社區(https://www.mwcxs.top),關注我們每天閱讀更多精彩內容】
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/104888.html
摘要:寫在前面專題系列是我寫的第二個系列,第一個系列是深入系列。專題系列自月日發布第一篇文章,到月日發布最后一篇,感謝各位朋友的收藏點贊,鼓勵指正。 寫在前面 JavaScript 專題系列是我寫的第二個系列,第一個系列是 JavaScript 深入系列。 JavaScript 專題系列共計 20 篇,主要研究日常開發中一些功能點的實現,比如防抖、節流、去重、類型判斷、拷貝、最值、扁平、柯里...
摘要:種常用數組去重第一種兩個循環思路新建一個為空的結果數組外層遍歷原數組,內層循環遍歷返回數組判斷內層循環數組當前元素和外層數組元素的值是否相等,是退出內層循環經過第二部后,此時內層循環數組的索引值和返回數組的長度正好相等,外層數組元素也是唯一 8 種常用數組去重 第一種 【兩個 for 循環】 思路: 新建一個為空的結果數組 外層 for 遍歷原數組,內層循環遍歷返回數組 判斷內層循環...
摘要:設計模式是以面向對象編程為基礎的,的面向對象編程和傳統的的面向對象編程有些差別,這讓我一開始接觸的時候感到十分痛苦,但是這只能靠自己慢慢積累慢慢思考。想繼續了解設計模式必須要先搞懂面向對象編程,否則只會讓你自己更痛苦。 JavaScript 中的構造函數 學習總結。知識只有分享才有存在的意義。 是時候替換你的 for 循環大法了~ 《小分享》JavaScript中數組的那些迭代方法~ ...
摘要:專題系列共計篇,主要研究日常開發中一些功能點的實現,比如防抖節流去重類型判斷拷貝最值扁平柯里遞歸亂序排序等,特點是研究專題之函數組合專題系列第十六篇,講解函數組合,并且使用柯里化和函數組合實現模式需求我們需要寫一個函數,輸入,返回。 JavaScript 專題之從零實現 jQuery 的 extend JavaScritp 專題系列第七篇,講解如何從零實現一個 jQuery 的 ext...
閱讀 1312·2021-10-08 10:05
閱讀 4135·2021-09-22 15:54
閱讀 3114·2021-08-27 16:18
閱讀 3116·2019-08-30 15:55
閱讀 1450·2019-08-29 12:54
閱讀 2758·2019-08-26 11:42
閱讀 556·2019-08-26 11:39
閱讀 2139·2019-08-26 10:11