This paper continues to analyze the methods of xOR family. The main purpose of XOR method is to realize xOR, also known as symmetric difference. These include XOR, xorBy, xorWith, and the core method baseXor.
Corresponding source code analysis has been pushed to github repository: github.com/MageeLin/lo…
The principle of
Mathematical principles
The mathematical principle of this method is actually very simple. The conversion method of two sets A and B to calculate the symmetry difference is as follows:
A ⊕ B = (A ∧ b) ∨ (¬ A ∧ B)
Similarly, the conversion of the three sets A, B and C to calculate the difference of symmetry is as follows:
A radius radius b c = ((a Sunday afternoon b) ∨ c) (a Sunday afternoon) ∨ ((b) Sunday afternoon a) ∨ (Sunday afternoon b, c)) ∨ ((c Sunday afternoon a) ∨ (c Sunday afternoon b))
Similarly, n sets can be divided into two steps to solve the symmetry difference. The first step is to combine each set with the non-intersection of all other sets, and the second step is to combine all the intersection sets.
In the implementation of LoDash, the first step is to realize the intersection of all arrays with other arrays by baseDifference. The second step is to flatten a layer and de-implement baseUniq.
Dependency path graph
The dependency path diagram for xOR family methods is shown below:
Just note the main dependencies on the left, which have been analyzed in previous articles.
The flow chart
The main flow chart for the implementation of the XOR family method is as follows:
The underlying method of dependency
baseXor
import baseDifference from './baseDifference.js';
import baseFlatten from './baseFlatten.js';
import baseUniq from './baseUniq.js';
/** ** the basic implementation of the xor family of methods, taking each array to be checked. * *@private
* @param {Array} The array checks each array *@param {Function} [iteratee] Iteratee calls each element *@param {Function} [comparator] The comparator calls each element *@returns {Array} Returns a new array */ with the filtered value
function baseXor(arrays, iteratee, comparator) {
// If the arrays array length is 1, baseUniq is called to return the array
const length = arrays.length;
if (length < 2) {
return length ? baseUniq(arrays[0] : []; }let index = -1;
const result = new Array(length);
/ / iteration arrays
while (++index < length) {
const array = arrays[index];
let othIndex = -1;
// Iterate over other arrays from the arrays array
while (++othIndex < length) {
if(othIndex ! = index) {Step 1: Set the corresponding position of result to array after comparing with baseDifferenceresult[index] = baseDifference( result[index] || array, arrays[othIndex], iteratee, comparator ); }}}// Core step 2: Flatten the result, i.e., expand each array
// Call baseUniq again to return
return baseUniq(baseFlatten(result, 1), iteratee, comparator);
}
export default baseXor;
Copy the code
The family of xor
The XOR family calls the baseXor core method.
xor
import baseXor from './.internal/baseXor.js';
import isArrayLikeObject from './isArrayLikeObject.js';
/ * * * create an array of a given array of unique value, * for a given multiple arrays [symmetric difference] (https://en.wikipedia.org/wiki/Symmetric_difference), * Creates an array of unique values. * The order of return values depends on the order in which they appear * *@since 2.4.0
* @category Array
* @param {... Array} [Arrays] The array to check *@returns {Array} Returns the filtered new array *@see difference, union, unionBy, unionWith, without, xorBy, xorWith
* @example
*
* xor([2, 1], [2, 3])
* // => [1, 3]
*/
function xor(. arrays) {
// Call baseXor directly and filter it so that only parameters of array type can be compared
return baseXor(arrays.filter(isArrayLikeObject));
}
export default xor;
Copy the code
xorBy
import baseXor from './.internal/baseXor.js';
import isArrayLikeObject from './isArrayLikeObject.js';
import last from './last.js';
/** * This method is similar to 'xor', but takes' iteratee 'as an argument. *' iteratee 'calls each value of each array and compares it with the new value generated. * 'iteratee' calls one argument :(value). * *@since 4.0.0
* @category Array
* @param {... Array} [Arrays] The array to check *@param {Function} Iteratee Iteratee calls each element *@returns {Array} Returns a new array * with the filtered value@see difference, union, unionBy, unionWith, without, xor, xorWith
* @example* * xorBy ([2.1, 1.2], [2.3, 3.4], Math. The floor) * / / = > [1.2, 3.4] * /
function xorBy(. arrays) {
// Take the last argument as iteratee
let iteratee = last(arrays);
// If the last argument is still an array, iteratee is undefined
if (isArrayLikeObject(iteratee)) {
iteratee = undefined;
}
// Filter the parameters and call baseXor
return baseXor(arrays.filter(isArrayLikeObject), iteratee);
}
export default xorBy;
Copy the code
xorWith
import baseXor from './.internal/baseXor.js';
import isArrayLikeObject from './isArrayLikeObject.js';
import last from './last.js';
/** * This method is similar to 'xor', but it takes a 'comparator' parameter. The * 'comparator' calls each element of each 'array' for comparison. * The order of return values depends on the order in which they appear in the array. * 'comparator' calls two arguments :(arrVal, othVal). * *@since 4.0.0
* @category Array
* @param {... Array} [Arrays] Each array to check *@param {Function} [comparator] The comparator calls each element *@returns {Array} Returns a new array * with the filtered value@see difference, union, unionBy, unionWith, without, xor, xorBy
* @example
*
* const objects = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }]
* const others = [{ 'x': 1, 'y': 1 }, { 'x': 1, 'y': 2 }]
*
* xorWith(objects, others, isEqual)
* // => [{ 'x': 2, 'y': 1 }, { 'x': 1, 'y': 1 }]
*/
function xorWith(. arrays) {
// Take the last parameter as comparator
let comparator = last(arrays);
// If the comparator is not function, use undefined
comparator = typeof comparator === 'function' ? comparator : undefined;
// Filter the parameters and call baseXor
return baseXor(arrays.filter(isArrayLikeObject), undefined, comparator);
}
export default xorWith;
Copy the code
A freestanding implementation
The native implementation can be simpler, using the associative law of symmetric difference:
A ⊕ B ⊕ C ⊕ D = (a ⊕ B) ⊕ C) ⊕ D
You can directly use reduce to calculate the symmetry difference with the previous symmetry difference result and the next array.
function xor(. arrays) {
let result = arrays.reduce((arrayAcc, arrayCur) = > {
let resultCur = []; // The result array of this iteration
// Find elements in ACC that are not in CUR
arrayAcc.forEach((item) = > {
if (arrayCur.indexOf(item) < 0) { resultCur.push(item); }});// Find elements from CUR that are not in ACC
arrayCur.forEach((item) = > {
if (arrayAcc.indexOf(item) < 0) { resultCur.push(item); }});return resultCur;
});
// go to return
return [...new Set(result)];
}
Copy the code
Front-end notepad, not regularly updated, welcome to pay attention to!
- Wechat official account: Lin Jingyi’s notepad
- Blog: Lin Jingyi’s notebook
- Nuggets column: Lin Jingyi’s notebook
- Zhihu column: Lin Jingyi’s notebook
- Github: MageeLin