摘要:引子數組去重是一個老生常談的話題,在面試中也經常會被問道。其中如果數組是排序的,去重運算效率更高,因為排序能夠將相同的數排列在一起,方便前后比較。當數組有序對于對象的去重,我們知道為,所以使用比較對象在實際場景中沒有意義。
引子
數組去重是一個老生常談的話題,在面試中也經常會被問道。對于去重,有兩種主流思想:
先排序,線性遍歷后去重,時間復雜度O(n*log2n);
使用哈希,空間換時間,時間復雜度O(n);
上一篇文章,我分析了underscore的函數是如何組織的,我們能夠依照這種方法書寫自己的函數庫,這篇文章,來看看關于函數去重underscore是如何做的?
Underscore的去重 功能介紹underscore的去重是指數組(Arrays)中uniq函數,其API如下:
uniq _.uniq(array, [isSorted], [iteratee]) 別名: unique
說明:返回 array去重后的副本, 使用 === 做相等測試. 如果您確定 array 已經排序, 那么給 isSorted 參數傳遞 true值, 此函數將運行的更快的算法. 如果要處理對象元素, 傳參 iterator 來獲取要對比的屬性.
上述API主要想說明幾點:
返回數組副本,不影響原數組
相等的標準是a===b,表明不僅要值相等,類型也需要相等
如果數組是排序的,去重運算效率更高
uniq也可以比較對象,前提是需要指定比較的對象屬性
我們簡單使用以下_.uniq(array, [isSorted], [iteratee]),如下:
console.log(_.uniq([1,4,2,2,3,3])); console.log(_.uniq([1,2,2,2,3,3],true)); console.log(_.uniq([{ name:1, gender:"male" },{ name:2, gender:"female" },{ name:2, gender:"male" },{ name:4, gender:"male" }],true,"gender"));
結果如下:
underscore去重的核心思想:
新建結果集數組res,遍歷待去重數組,將每個遍歷值在res數組中遍歷檢查,將不存在當前res中的遍歷值壓入res中,最后輸出res數組。
function uniq(array){ var res = []; array.forEach(function(element) { if(res.indexOf(element)<0){ res.push(element); } }, this); return res; } console.log(uniq([1,4,2,2,3,3])); //[1,4,2,3]
其中如果數組是排序的,去重運算效率更高,因為排序能夠將相同的數排列在一起,方便前后比較。
function uniq(array, isSorted) { var res = []; var seen = null; array.forEach(function (element,index) { if (isSorted) { //當數組有序 if(!index || seen !== element) res.push(element); seen = element; } else { if (res.indexOf(element) < 0) { res.push(element); } } }, this); return res; } console.log(uniq([1,2,"2",3,3,3,5],true)); //(5)?[1, 2, "2", 3, 5]
對于對象的去重,我們知道{}==={}為false,所以使用===比較對象在實際場景中沒有意義。
在這里我舉個實際場景的例子:
我要在小組中選擇一名男生(male)和一名女生(female),小組組員情況如下:
var array = [{ name:"Tom", gender:"female" },{ name:"Lucy", gender:"female" },{ name:"Edward", gender:"male" },{ name:"Molly", gender:"female" }]
我們修改上面的uniq:
function uniq(array, isSorted, iteratee) { var res = []; var seen = []; array.forEach(function (element, index) { if (iteratee) { //判斷iteratee是否存在,存在的話,取出真正要比較的屬性 var computed = element[iteratee]; if (seen.indexOf(computed) < 0) { seen.push(computed); res.push(element); } } else if (isSorted) { //當數組有序 if (!index || seen !== element) res.push(element); seen = element; } else { if (res.indexOf(element) < 0) { res.push(element); } } }, this); return res; } console.log(uniq([{ name:"Tom", gender:"female" },{ name:"Lucy", gender:"female" },{ name:"Edward", gender:"male" },{ name:"Molly", gender:"female" }],true,"gender"));
結果如下:
underscore的uniq的實現,基本上使用的上述思想。在附錄中我附上了源碼和一些注釋。
上述我分析了underscore的uniq函數實現,在這之前我也看過諸如《JavaScript去重的N種方法》...之類的文章,underscore中的uniq函數實現方法并不是最優解,至少從時間復雜度來講不是最優。
那么為什么underscore不用Set對象來解決去重問題,使用indexof查找的時間復雜度是O(n),而hash查詢是O(1)。
我個人認為Set是ES6中引的對象,underscore是為了考慮兼容性問題。
那為什么不用obj作為Set的替代方案呢?
這里我猜是underscore的設計者只想用自己內部實現的_.indexOf函數。此處是我的猜測,大家如果有想法,歡迎大家留言!
下面我附上ES6的實現(大家最熟悉的):
var a = [1,1,2,3,4,4]; var res = [...new Set(a)];
再附上obj的實現:
function uniq(array,iteratee){ var res = []; var obj = {}; array.forEach(function(element) { var computed = element; if(iteratee) computed = element[iteratee]; if(!obj.hasOwnProperty(computed)) obj[(typeof computed)+"_"+JSON.stringify(computed)] = element; }, this); for(var p in obj){ res.push(obj[p]); } return res; } uniq([1,"1",2,3,4,4]);// (5)?[1, "1", 2, 3, 4]附錄
underscore的uniq函數源碼及注釋:
_.uniq = _.unique = function(array, isSorted, iteratee, context) { if (array == null) return []; if (!_.isBoolean(isSorted)) { //如果沒有排序 context = iteratee; iteratee = isSorted; isSorted = false; } /** ** 此處_.iteratee ** function (key){ * return function(obj){ * return obj[key]; * } ** } ** key就是這里的iteratee(對象的屬性),這里使用了閉包 **/ if (iteratee != null) iteratee = _.iteratee(iteratee, context); var result = [];//返回去重后的數組(副本) var seen = []; for (var i = 0, length = array.length; i < length; i++) { var value = array[i];//當前比較值 if (isSorted) { //如果i=0時,或者seen(上一個值)不等于當前值,放入去重數組中 if (!i || seen !== value) result.push(value); seen = value;//保存當前值,用于下一次比較 } else if (iteratee) { var computed = iteratee(value, i, array); if (_.indexOf(seen, computed) < 0) { seen.push(computed); result.push(value); } } else if (_.indexOf(result, value) < 0) { result.push(value); } } return result; };
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/102527.html
摘要:寫在前面專題系列是我寫的第二個系列,第一個系列是深入系列。專題系列自月日發布第一篇文章,到月日發布最后一篇,感謝各位朋友的收藏點贊,鼓勵指正。 寫在前面 JavaScript 專題系列是我寫的第二個系列,第一個系列是 JavaScript 深入系列。 JavaScript 專題系列共計 20 篇,主要研究日常開發中一些功能點的實現,比如防抖、節流、去重、類型判斷、拷貝、最值、扁平、柯里...
摘要:而數組元素去重是基于運算符的。而如果有迭代函數,則計算傳入迭代函數后的值,對值去重,調用方法,而該方法的核心就是調用方法,和我們上面說的方法一異曲同工。 Why underscore (覺得這部分眼熟的可以直接跳到下一段了...) 最近開始看 underscore.js 源碼,并將 underscore.js 源碼解讀 放在了我的 2016 計劃中。 閱讀一些著名框架類庫的源碼,就好像...
摘要:專題系列共計篇,主要研究日常開發中一些功能點的實現,比如防抖節流去重類型判斷拷貝最值扁平柯里遞歸亂序排序等,特點是研究專題之函數組合專題系列第十六篇,講解函數組合,并且使用柯里化和函數組合實現模式需求我們需要寫一個函數,輸入,返回。 JavaScript 專題之從零實現 jQuery 的 extend JavaScritp 專題系列第七篇,講解如何從零實現一個 jQuery 的 ext...
摘要:支持兩種不同風格的函數調用在中我們可以使用以下兩種方式調用函數式的調用對象式調用在中,它們返回的結果都是相同的。 原文:https://zhehuaxuan.github.io/... 作者:zhehuaxuan 目的 Underscore 是一個 JavaScript 工具庫,它提供了一整套函數式編程的實用功能,但是沒有擴展任何 JavaScript 內置對象。 本文主要梳理und...
摘要:今天要講的是數組展開以及和數組展開息息相關的一個重要的內部方法。也是個布爾值,當為并且也為時,能過濾參數元素中的非數組元素。首先并不需要對數組深度展開,其次傳入的是數組,對于非數組元素可以直接忽略。 Why underscore (覺得這一段眼熟的童鞋可以直接跳到正文了...) 最近開始看 underscore.js 源碼,并將 underscore.js 源碼解讀 放在了我的 201...
閱讀 651·2021-11-23 09:51
閱讀 3597·2021-11-15 11:38
閱讀 925·2021-10-14 09:42
閱讀 3160·2021-09-29 09:35
閱讀 2103·2021-09-03 10:33
閱讀 769·2021-07-30 16:33
閱讀 1557·2019-08-30 15:55
閱讀 1840·2019-08-30 14:04