利用Underscore求陣列的交集、並集和差集

Russ_Zhong發表於2019-03-04

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中已經有了很多工具方法,所以可以拿來直接使用,比如restArgsflattenuniq。為什麼強調這幾個方法呢?因為使用這幾個方法就可以實現陣列求並集。

我們的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

相關文章