preface
The countBy function creates an object that conditionally counts the value of some of the types in the first collection. The key of the object is the type of the value of the second iteratee. The corresponding value is the number of times each key of the first argument is returned after iteratee processes the second argument
Thought analysis
Source code analysis
1. countBy
1. Pass in parameters
Collection is passed a collection of iterators of type array, class array, plain object, etc. This can be interpreted as a condition for countBy aggregation
2. Source code analysis
CreateAggregator is called, and the count function is passed in. If result does not have a key, the baseAssignValue is initialized to 1. If result does have a key, the baseAssignValue is initialized to 1
var countBy = createAggregator(function(result, value, key) {
if (hasOwnProperty.call(result, key)) {
++result[key];
} else {
baseAssignValue(result, key, 1); }});Copy the code
2. createAggregator
1. Pass in parameters
setter
对accumulator
The function that the accumulator processes,initalizer
Initializer function that initializes an accumulator for recording, default is an object
2. Source code analysis
An aggregator function is created, and initalizer is not passed in here so a new object is created as a record of the data by default. Using setter counts here the aggregator function internally determines whether the collection is an array. If it’s an array, use arrayAggregator, if it’s not, use baseAggregator. So I have a question here, why don’t I just do isArrayLike, so I don’t have to do baseAggregator again, And arrayAggregator is perfectly up to the task
function createAggregator(setter, initializer) {
return function(collection, iteratee) {
var func = isArray(collection) ? arrayAggregator : baseAggregator,
accumulator = initializer ? initializer() : {};
return func(collection, setter, getIteratee(iteratee, 2), accumulator);
};
}
Copy the code
3. getIteratee
1. Pass in parameters
value
The value to be processed by the iteratorarity
The number of iterators created
2. Source code analysis
Iteratee: iteratee: iteratee: iteratee: iteratee: iteratee: iteratee: iteratee: iteratee: iteratee: iteratee: iteratee: iteratee: iteratee: iteratee: iteratee: iteratee: iteratee: iteratee: iteratee: iteratee: iteratee It is also one of the most complex functions in LoDash, and I’ll talk about how to use it later
function getIteratee() {
var result = lodash.iteratee || iteratee;
result = result === iteratee ? baseIteratee : result;
return arguments.length ? result(arguments[0].arguments[1]) : result;
}
Copy the code
4. arrayAggregator
1. Pass in parameters
array
Regular arraysetter
To deal withaccumulator
The function ofiteratee
Iterators, andcountBy
Is consistent with the iteratoraccumulator
Accumulator, record the final result
2. Source code analysis
In setter counting, the accmulator is the object that counts, the key is the return value of iteratee(value), and the final return accumulator. The accmulator function internally conditions length and uses a while loop
function arrayAggregator(array, setter, iteratee, accumulator) {
var index = -1,
length = array == null ? 0 : array.length;
while (++index < length) {
var value = array[index];
setter(accumulator, value, iteratee(value), array);
}
return accumulator;
}
Copy the code
5. baseAggregator
1. Pass in parameters
collection
Class array object or objectsetter
To deal withaccumulator
The function ofiteratee
Iterators, andcountBy
Is consistent with the iteratoraccumulator
Accumulator, record the final result
2. Source code analysis
BaseEach determines whether the collection is an array of classes. If not, createBaseFor fetches all the keys in the collection and their total. A while loop iterates through all the keys to the value and returns the counter
function baseAggregator(collection, setter, iteratee, accumulator) {
baseEach(collection, function(value, key, collection) {
setter(accumulator, value, iteratee(value), collection);
});
return accumulator;
}
Copy the code
6. baseAssignValue
1. Pass in parameters
object
An objectkey
Need to mergekey
值value
Previous parameterkey
Value of the correspondingvalue
值
2. Source code analysis
Merge attributes of __proto__ using defineProperty on the native object, and merge other values of the object directly with []
function baseAssignValue(object, key, value) {
if (key == '__proto__' && defineProperty) {
defineProperty(object, key, {
'configurable': true.'enumerable': true.'value': value,
'writable': true
});
} else{ object[key] = value; }}Copy the code
Application scenarios
Similarly, count the number of occurrences of the same number in the integer part of the array, and the number of occurrences of the same length of the string
_.countBy([6.1.4.2.6.3].Math.floor);
// => {'4': 1, '6': 2}
_.countBy(['one'.'two'.'three'].'length');
// => {'3': 2, '5': 1}
Copy the code
conclusion
CountBy passes in iteratee to filter out and count the key values according to certain conditions, which is an expression of functional programming. The traditional way of counting might be to create a new function to iterate over iteratee and then pass it into count. The idea of functional programming is to use countBy to pass iteratee directly as a condition and use a function to do the counting directly