天天看點

利用Underscore求數組的交集、并集和差集

1 數組交集函數——intersection

數組的交集是指包含多個數組中的共同元素的一個數組,求數組的交集就是找出給定數組中的共有元素。

下面實作一個求兩個數組交集的函數。

判斷數組是夠包含指定值,使用Array.indexOf就可以。是以我們可以周遊第一個參數數組,然後使用Array.indexOf方法檢索第二個參數數組,如果第二個參數數組包含目前項,那麼目前項即為兩個數組的交集元素,放入結果數組即可:

var intersection = function(arr1, arr2) {
    var length = arr1.length;
    var result = [];
    var i;
    for(i = 0; i < length; i++) {
        if(result.indexOf(arr1[i]) >= 0) 
            continue;
        else {
            if(arr2.indexOf(arr1[i]) >= 0)
                result.push(arr1[i]);
        }
    }
    return result;
}
複制代碼
           

以上代碼實作了求兩個數組交集的功能。

如果涉及到多個數組呢?那就是Underscore的實作方法了。

以下是Underscore的源碼(附注釋):

// Produce an array that contains every item shared between all the
// passed-in arrays.
//擷取傳入的多個數組的交集,之是以隻有一個形參,是因為該函數使用第一個數組參數作為基準。
_.intersection = function (array) {
	//将要傳回的結果數組。
	var result = [];
	//傳入數組的個數。
	var argsLength = arguments.length;
	//周遊第一個數組參數。
	for (var i = 0, length = getLength(array); i < length; i++) {
		//目前項。
		var item = array[i];
		//如果結果數組中已有該項,那麼直接跳過目前循環,進入下一輪循環中。
		if (_.contains(result, item)) continue;
		var j;
		//從第二個參數開始,周遊每一個參數。
		for (j = 1; j < argsLength; j++) {
			//一旦有一個參數數組不包含item,就退出循環。
			if (!_.contains(arguments[j], item)) break;
		}
		//如果所有參數數組都包含item項,就把item放入result。
		if (j === argsLength) result.push(item);
	}
	return result;
};
複制代碼
           

可以看到該函數一次接受多個數組,但是隻有一個形參(array),該參數表示接收到的第一個數組,Underscore使用它作為參考,周遊該數組,然後依次判斷剩餘參數數組是否包含目前項,如果全部包含則該項為交集元素,推入結果數組當中。

2 數組并集函數——union

數組的并集是指包含指定的多個數組的所有元素的數組,求多個數組的并集即為求一個包含所有數組的所有元素的數組。

這裡最直接的實作方法就是周遊所有數組參數,然後針對數組的每一項,放入到結果數組中(如果已經存在于結果數組中那麼久不再添加)。

var union = function() {
	var arrays = arguments;
	var length = arguments.length;
	var result = [];
	var i;
	for(i = 0; i < length; i++) {
		var arr = arrays[i];
		var arrLength = arrays[i].length;
		for(var j = 0; j < arrLength; j++) {
			if(result.indexOf(arr[j]) < 0) {
				result.push(arr[j]);
			}
		}
	}
	return result;
}
複制代碼
           

在閱讀Underscore源碼的時候,感覺它的實作方法十分巧妙。

Underscore中已經有了很多工具方法,是以可以拿來直接使用,比如restArgs、flatten、uniq。為什麼強調這幾個方法呢?因為使用這幾個方法就可以實作數組求并集。

我們的union方法是接受多個數組作為參數的,而restArgs可以把多個數組參數合并到一個數組中作為參數;然後通過flatten函數,我們可以把得到的這個數組參數展開,展開之後得到的數組就是包含所有數組參數的所有元素的一個數組了,但是這個數組中有備援項,我們必須對其進行去重;這時候使用我們的uniq工具函數就可以對其進行去重了。

經過這三個函數的處理,我們得到的數組就是多個數組參數的并集!

Underscore源碼:

// Produce an array that contains the union: each distinct element from all of
// the passed-in arrays.
_.union = restArgs(function (arrays) {
	return _.uniq(flatten(arrays, true, true));
});
複制代碼
           

這樣的實作是不是很簡介大氣?

3 數組差集函數——difference

數組的差集是指由數組A中所有不屬于數組B的元素所組成的一個數組。

直接的實作方法就是周遊前者,然後判斷每個元素是否屬于後者,如果不屬于,那麼就推入結果數組。

簡單實作:

var difference = function(arr1, arr2) {
	var length = arr1.length;
	var i;
	var result = [];
	for(i = 0; i < length; i++) {
		if(arr2.indexOf(arr1[i]) < 0) {
			result.push(arr1[i]);
		}
	}
	return result;
}
複制代碼
           

Underscore的實作(附注釋):

// Take the difference between one array and a number of other arrays.
// Only the elements present in just the first array will remain.
//數組求差集函數。
//通過restArgs函數把第二個數組開始的所有參數數組合并到一個數組。
_.difference = restArgs(function (array, rest) {
	//使用flatten展開rest數組。
	rest = flatten(rest, true, true);
	//使用filter函數過濾array數組達到求差集的目的,判斷條件就是value是否屬于rest。
	return _.filter(array, function (value) {
		return !_.contains(rest, value);
	});
});
複制代碼
           

更多Underscore源碼解析:GitHub