This article will continue to analyze the zip family of methods, the zip method is mainly to synthesize the target array groups into arrays or objects. Methods for composing arrays include zip, unzip, zipWith, and unzipWith. Methods for composing objects include zipObject, zipObjectDeep, and core methods baseZipObject and baseSet.
Corresponding source code analysis has been pushed to github repository: github.com/MageeLin/lo…
The dependency path diagram for the ZIP family method is shown below:
zip
The zip method is interesting in that it is the same operation as unzip while being the inverse. Inside the zip method is the called return Unzip (Arrays).
unzip
Unzip is the basis for zip, zipWith, and unzipWith implementations. But unzip is essentially a two-step process:
- find
arrays
In the longestarray
The length of the; - from
arrays
Each of thearray
Take out the corresponding position of the elements assembled together to form a newarrays
;
import filter from './filter.js';
import map from './map.js';
import baseProperty from './.internal/baseProperty.js';
import isArrayLikeObject from './isArrayLikeObject.js';
/** * This method is similar to 'zip', but it takes' arrays' from 'array' and creates a 'arrays' from' array ', which is the grouping structure before zip **@since 1.2.0
* @category Array
* @param {Array} Array The array * to be executed@returns {Array} Returns the regrouped array *@see unzipWith, zip, zipObject, zipObjectDeep, zipWith
* @example
*
* const zipped = zip(['a', 'b'], [1, 2], [true, false])
* // => [['a', 1, true], ['b', 2, false]]
*
* unzip(zipped)
* // => [['a', 'b'], [1, 2], [true, false]]
*/
function unzip(array) {
Return [] when array is an empty array
if(! (array ! =null && array.length)) {
return [];
}
let length = 0;
// The iteration performs two functions. The first is to filter out the array
// The second is to set length to the length of the largest array
array = filter(array, (group) = > {
if (isArrayLikeObject(group)) {
length = Math.max(group.length, length);
return true; }});let index = -1;
// Create an empty array based on the longest length
const result = new Array(length);
/ / iteration
while (++index < length) {
// 把每一个array都取对应的位置处的值map成一个新array
// Then assign result[index]
result[index] = map(array, baseProperty(index));
}
return result;
}
export default unzip;
Copy the code
zip
The zip method and unzip are both inverse operations, but they are the same operation. The reason is that essentially all the arrays are called unzip, but when the same arrays are repeatedly called by Unzip, they actually repeatedly form two arrays.
import unzip from './unzip.js';
/** * Create a grouping of 'arrays' whose first array contains all the first elements of a given array, whose second array contains all the second elements of a given array, and so on. * *@since 0.1.0 from *@category Array
* @param {... Array} [Arrays] The arrays * to deal with@returns {Array} Returns the arrays * of the grouped arrays@see unzip, unzipWith, zipObject, zipObjectDeep, zipWith
* @example
*
* zip(['a', 'b'], [1, 2], [true, false])
* // => [['a', 1, true], ['b', 2, false]]
*/
function zip(. arrays) {
// Returns the result of unzip(Arrays), which is essentially the same as ZIP
return unzip(arrays);
}
export default zip;
Copy the code
unZipWith
The same principle applies to unZipWith and zipWith. But they all rely on unZip, just calling Iteratee once each time through the loop.
import map from './map.js';
import unzip from './unzip.js';
/** * This method is similar to 'unzip', but it takes an 'iteratee' iterative method argument * to specify how the recombined values should be combined. * Iteratee calls pass in the value of each group: (... Group). * *@since 3.8.0
* @category Array
* @param {... Array} [Arrays] The arrays * to deal with@param {Function} The iteratee method iterates through each array, deciding how to combine the grouping values *@returns {Array} Returns the regrouped array *@example
*
* const zipped = zip([1, 2], [10, 20], [100, 200])
* // => [[1, 10, 100], [2, 20, 200]]
*
* unzipWith(zipped, add)
* // => [3, 30, 300]
*/
function unzipWith(array, iteratee) {
Return [] when array is an empty array
if(! (array ! =null && array.length)) {
return [];
}
Call unzip directly to return the result
const result = unzip(array);
// Return iteratee.apply(undefined, group) to convert the results in the group
return map(result, (group) = > iteratee.apply(undefined, group));
}
export default unzipWith;
Copy the code
zipWith
The thing to watch out for in zipWith is the iteratee argument, and if the last argument is a function, pop it out of the Arrays.
import unzipWith from './unzipWith.js';
/** * This method is similar to 'zip', but it takes an 'iteratee' (iterating method), * to specify how grouped values should be combined. * Iteratee calls the elements of each group: (... group). * *@since 3.8.0
* @category Array
* @param {... Array} [Arrays] The arrays * to deal with@param {Function} The iteratee method iterates through each array, deciding how to combine the grouping values *@returns {Array} Returns the arrays * of the grouped arrays@see unzip, unzipWith, zip, zipObject, zipObjectDeep, zipWith
* @example
*
* zipWith([1, 2], [10, 20], [100, 200], (a, b, c) => a + b + c)
* // => [111, 222]
*/
function zipWith(. arrays) {
// Get the last argument iteratee and pop it out of the Arrays array
const length = arrays.length;
let iteratee = length > 1 ? arrays[length - 1] : undefined;
iteratee =
typeof iteratee === 'function' ? (arrays.pop(), iteratee) : undefined;
// unzipWith(Arrays, iteratee)
return unzipWith(arrays, iteratee);
}
export default zipWith;
Copy the code
zipObject
The zipObject method does something different from the Zip family. ZipObject takes two arguments, the first an array of keys, the second an array of values, and finally an object.
Here are the pre-dependent methods assignValue for ordinary assignments and baseSet for deep assignments.
baseAssignValue
__proto__ should be false as an enumerable: true. We need to rethink this.
/** * the basic implementation of 'assignValue' and 'assignMergeValue' does not check the value **@private
* @param {Object} Object Indicates the object * to be modified@param {string} Key Specifies the attribute key * to be assigned@param {*} The value to assign */
function baseAssignValue(object, key, value) {
// Use object.defineProperty () when assigning a value to an Object's prototype
if (key == '__proto__') {
Object.defineProperty(object, key, {
configurable: true.enumerable: true.value: value,
writable: true}); }else {
// If it is normal, it can be allocated directlyobject[key] = value; }}export default baseAssignValue;
Copy the code
assignValue
import baseAssignValue from './baseAssignValue.js';
import eq from '.. /eq.js';
/** used to check the object's own properties */
const hasOwnProperty = Object.prototype.hasOwnProperty;
/** * Assign 'value' to 'key' on 'object' when the corresponding value is not the same as' value '@private
* @param {Object} Object Indicates the object * to be modified@param {string} Key Specifies the attribute key * to be assigned@param {*} Value Value of the attribute to be assigned */
function assignValue(object, key, value) {
// Get the corresponding attribute value of object
const objValue = object[key];
// When the key is not an object property, or the value to be assigned is not equal to the corresponding value of the objValue
if(! (hasOwnProperty.call(object, key) && eq(objValue, value))) {// When value is a significant number
if(value ! = =0 || 1 / value === 1 / objValue) {
/ / execution baseAssignValue ()
baseAssignValue(object, key, value);
}
// Or if value is undefined and object has no key
} else if (value === undefined && !(key in object)) {
/ / execution baseAssignValue ()baseAssignValue(object, key, value); }}export default assignValue;
Copy the code
baseSet
import assignValue from './assignValue.js';
import castPath from './castPath.js';
import isIndex from './isIndex.js';
import isObject from '.. /isObject.js';
import toKey from './toKey.js';
/** ** 'set' base implementation **@private
* @param {Object} Object Indicates the object * to be modified@param {Array|string} Path Path of the property to be set *@param {*} Value Value of the property to be set *@param {Function} [customizer] Custom path creation method *@returns {Object} Returns the object */
function baseSet(object, path, value, customizer) {
// If it is not an object, return it directly
if(! isObject(object)) {return object;
}
// Returns an array of paths
path = castPath(path, object);
const length = path.length;
const lastIndex = length - 1;
let index = -1;
let nested = object;
// The path array is used to add descendant attributes to the object.
// Object, nested, objValue are all on the original object
// value, newValue(change) are on the property to be set
while(nested ! =null && ++index < length) {
const key = toKey(path[index]);
let newValue = value;
// How to sort the newValue of each layer
if(index ! = lastIndex) {// Nested is the object of each layer
const objValue = nested[key];
// If there is a custom path to create a method, use undefined, convenient next step
newValue = customizer ? customizer(objValue, key, nested) : undefined;
// Start creation when there is no next level path on the object
if (newValue === undefined) {
newValue = isObject(objValue)
? // If there is a key on the original object, assign it directly to newValue
objValue
: // index represents an empty array, otherwise an empty object
isIndex(path[index + 1])? [] : {}; } } assignValue(nested, key, newValue); nested = nested[key]; }return object;
}
export default baseSet;
Copy the code
baseZipObject
/** * the base implementation of zipObject that assigns attribute values using 'assignFunc'. * *@private
* @param {Array} The props property identifier *@param {Array} Values Attribute value *@param {Function} AssignFunc is a function that assigns attributes@returns {Object} Return a new object */
function baseZipObject(props, values, assignFunc) {
/ / initialization
let index = -1;
const length = props.length;
const valsLength = values.length;
// Create a result object
const result = {};
/ / iteration
while (++index < length) {
// normalize the value of values
const value = index < valsLength ? values[index] : undefined;
// Assign values using assignFunc
assignFunc(result, props[index], value);
}
return result;
}
export default baseZipObject;
Copy the code
zipObject
When the zipObject method calls baseZipObject, it passes assignValue, so assignment is performed directly on each element of the props as a key.
import assignValue from './.internal/assignValue.js';
import baseZipObject from './.internal/baseZipObject.js';
/** * This method is similar to 'fromPairs', but it takes two arrays, * the values in the first array are the property identifiers (property names) and the values in the second array are the corresponding property values. *@since 0.4.0
* @category Array
* @param {Array} [props=[]] Property identifier array *@param {Array} [values=[]] Array of attribute values *@returns {Object} Return a new object *@see unzip, unzipWith, zip, zipObjectDeep, zipWith
* @example
*
* zipObject(['a', 'b'], [1, 2])
* // => { 'a': 1, 'b': 2 }
*/
function zipObject(props, values) {
return baseZipObject(props || [], values || [], assignValue);
}
export default zipObject;
Copy the code
zipObjectDeep
The zipObjectDeep method, when calling baseZipObject, passes baseSet, so the depth assignment is based on the different form of each element of the props.
import baseSet from './.internal/baseSet.js';
import baseZipObject from './.internal/baseZipObject.js';
/** * This method is similar to 'zipObject', but it supports property paths. * *@since 4.1.0
* @category Array
* @param {Array} []] Attribute identifier *@param {Array} [values=[]] Attribute value *@returns {Object} Returns a new object *@see unzip, unzipWith, zip, zipObject, zipWith
* @example
*
* zipObjectDeep(['a.b[0].c', 'a.b[1].d'], [1, 2])
* // => { 'a': { 'b': [{ 'c': 1 }, { 'd': 2 }] } }
*/
function zipObjectDeep(props, values) {
// baseZipObject() is also called, but the assignment method uses baseSet
return baseZipObject(props || [], values || [], baseSet);
}
export default zipObjectDeep;
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