Common front-end tool library and high-frequency interview questions (continuously updated...)
Published: 2024-05-06 08:06:10

> Front-end common tool library and high-frequency interview questions (continuously updated..., welcome to star collection)

Source code address: [https://github.com/niexq/coding-interview-questions](https://github.com/niexq/coding-interview-questions), thank you for reading, welcome `Star` to not get lost😄

# 🔥 coding-interview-questions

Common front-end tool library and high-frequency interview questions

### Table of contents
+ 1、✅ Lodash function list
+ 2、💻 Ramda function list
+ 3、💻 JavaScript Advanced Programming 4th Edition Study Notes
+ 4、💻 React interview questions

##Lodash

> Lodash is a JavaScript tool library

### Lodash function list

### Array

### chunk

Split an array into multiple smaller arrays

```js
const chunk = (arr, size) =>
   Array.from({ length: Math.ceil(arr.length / size) }, (v, i) =>
     arr.slice(i * size, i * size + size)
   );

chunk(['a', 'b', 'c', 'd'], 2);
// => [['a', 'b'], ['c', 'd']]

chunk(['a', 'b', 'c', 'd'], 3);
// => [['a', 'b', 'c'], ['d']]
```

Idea: Cut the array into multiple small arrays according to the specified size. Use the Array.from() method of ES6 to create a new array with a length equal to the number of arrays after cutting. Use the slice() method to cut the original array according to the size after cutting. Divide by size and return each small array stored in a new array.

### compact

Remove false values (false, null, 0, "", undefined, NaN) from the array

```js
const compact = arr => arr.filter(Boolean);

compact([0, 1, false, 2, '', 3]);
// => [1, 2, 3]
```

Idea: Use the filter() method to filter out the true values in the array. The Boolean() function converts all false values into false and all true values into true.

###concat

Merge multiple arrays

```js
const concat = (...args) => [].concat(...args);

const array = [1];
const other = concat(array, 2, [3], [[4]]);

console.log(other);
// => [1, 2, 3, [4]]

console.log(array);
// => [1]
```

Idea: Use the ES6 spread operator (...) to convert the incoming parameters into an array, and then use the concat() method to combine all arrays into a new array and return it.

### difference

Returns an array containing elements that are in the first array but not in the other arrays

```js
// // The first implementation
// const difference = (arr, ...args) => arr.filter((item) => !args.flat().includes(item));
// 2nd implementation
const difference = (arr, ...args) =>
   arr.filter(item => args.every(arg => !arg.includes(item)));

difference([3, 2, 1], [4, 2]);
// => [3, 1]
```

Idea: Use the filter() method to traverse the first array, use the every() method to traverse other arrays, filter out the elements in the first array that are not included in other arrays, and return a new array.

### differenceBy

Similar to difference, but you can specify a function to compare the elements in the array

```js
const differenceBy = (array, values, iteratee) => {
   const fn = typeof iteratee === 'function' ? iteratee : item => item[iteratee];
   const valuesSet = new Set(values.map(fn));
   return array.filter(item => !valuesSet.has(fn(item)));
};

differenceBy([3.1, 2.2, 1.3], [4.4, 2.5], Math.floor);
// => [3.1, 1.3]

// The `property` iteratee shorthand.
differenceBy([{ x: 2 }, { x: 1 }], [{ x: 1 }], 'x');
// => [{ 'x': 2 }]
```

Idea: First determine whether the third parameter is a function. If so, use the function to compare the elements in the array for differential filtering. Otherwise, directly use lodash's differential filtering function to implement it.

### differenceWith

Filter out elements from the first array that are not in the second array, using a custom comparison function to compare.

```js
const differenceWith = (array, values, comparator) =>
   array.filter(item => !values.some(value => comparator(item, value)));

const objects = [
   { x: 1, y: 2 },
   { x: 2, y: 1 },
];

_.differenceWith(objects, [{ x: 1, y: 2 }], _.isEqual);
// => [{ 'x': 2, 'y': 1 }]
```

Idea: Use the higher-order functions filter and some to compare two arrays and return the elements in the first array that are not included in the second array.

### drop

Returns a new array, removing the first n elements from the original array

```js
const drop = (arr, n = 1) => arr.slice(n);

drop([1, 2, 3]);
// => [2, 3]

drop([1, 2, 3], 2);
// => [3]

drop([1, 2, 3], 5);
// => []

drop([1, 2, 3], 0);
// => [1, 2, 3]
```

Idea: Use the slice() method to delete the first n elements in the original array and return them.

### dropRight

Returns a new array, removing the last n elements from the original array

```js
const dropRight = (arr, n = 1) =>
   n >= arr.length ? [] : arr.slice(0, arr.length - n);

dropRight([1, 2, 3]);
// => [1, 2]

dropRight([1, 2, 3], 2);
// => [1]

dropRight([1, 2, 3], 5);
// => []

dropRight([1, 2, 3], 0);
// => [1, 2, 3]
```

Idea: According to the value of n, obtain a new array through the slice method of the array, thereby achieving the operation of deleting the last element.

### dropRightWhile

Returns a new array, removing the elements from the last qualifying element to the end of the original array

```js
const dropRightWhile = (array, iteratee) => {
   let right = array.length - 1;
   if (typeof iteratee === 'function') {
     while (iteratee(array[right])) {
       right--;
     }
   }
   if (typeof iteratee === 'object' && !Array.isArray(iteratee)) {
     const entries = Object.entries(iteratee);
     while (entries.every(([key, value]) => array[right][key] === value)) {
       right--;
     }
   }
   if (Array.isArray(iteratee) && iteratee.length === 2) {
     const [key, value] = iteratee;
     while (array[right][key] === value) {
       right--;
     }
   }
   return array.slice(0, right + 1);
};

const users = [
   { user: 'barney', active: true },
   { user: 'fred', active: false },
   { user: 'pebbles', active: false },
];

dropRightWhile(users, o => !o.active);
// => objects for ['barney']

// The `matches` iteratee shorthand.
dropRightWhile(users, { user: 'pebbles', active: false });
// => objects for ['barney', 'fred']

// The `matchesProperty` iteratee shorthand.
dropRightWhile(users, ['active', false]);
// => objects for ['barney']

// The `property` iteratee shorthand.
dropRightWhile(users, 'active');
// => objects for ['barney', 'fred', 'pebbles']
```

Idea: This function implements a function that starts traversing from the end of the array, and when the element meets the passed iterator condition, deletes the element from the array and returns the new array after deletion. Iterators can be functions, objects, or arrays.

###fill

Fill an array with specified values

```js
const fill = (arr, value, start = 0, end = arr.length) =>
   arr.fill(value, start, end);

const array = [1, 2, 3];

fill(array, 'a');
console.log(array);
// => ['a', 'a', 'a']

fill(Array(3), 2);
// => [2, 2, 2]

fill([4, 6, 8, 10], '*', 1, 3);
// => [4, '*', '*', 10]
```

Idea: Use the fill() method to replace the elements from the specified position to the specified position of the array with the specified value, and return the modified array.

### findIndex

Returns the index of the first element that meets the criteria

```js
const findIndex = (arr, fn) => arr.findIndex(fn);

const users = [
   { user: 'barney', active: false },
   { user: 'fred', active: false },
   { user: 'pebbles', active: true },
];

findIndex(users, o => o.user === 'barney');
// => 0

// The `matches` iteratee shorthand.
findIndex(users, { user: 'fred', active: false });
// => 1

// The `matchesProperty` iteratee shorthand.
findIndex(users, ['active', false]);
// => 0

// The `property` iteratee shorthand.
findIndex(users, 'active');
// => 2
```

Idea: Use the findIndex() method to find the subscript of the element that meets the conditions in the array, and return -1 if not found.

### findLastIndex

Returns the index of the last element that meets the criteria

```js
const findLastIndex = (arr, predicate) => {
   if (typeof predicate === 'function') {
     for (let i = arr.length - 1; i >= 0; i--) {
       if (predicate(arr[i], i, arr)) {
         return i;
       }
     }
   } else if (Array.isArray(predicate)) {
     const [key, value] = predicate;
     for (let i = arr.length - 1; i >= 0; i--) {
       if (arr[i][key] === value) {
         return i;
       }
     }
   } else if (typeof predicate === 'object') {
     for (let i = arr.length - 1; i >= 0; i--) {
       const keys = Object.keys(predicate);
       const match = keys.every(key => predicate[key] === arr[i][key]);
       if (match) {
         return i;
       }
     }
   } else {
     for (let i = arr.length - 1; i >= 0; i--) {
       if (arr[i] && arr[i][predicate]) {
         return i;
       }
     }
   }
   return -1;
};

const users = [
   { user: 'barney', active: true },
   { user: 'fred', active: false },
   { user: 'pebbles', active: false },
];

findLastIndex(users, o => o.user === 'pebbles');
// => 2

// The `matches` iteratee shorthand.
findLastIndex(users, { user: 'barney', active: true });
// => 0

// The `matchesProperty` iteratee shorthand.
findLastIndex(users, ['active', false]);
// => 2

// The `property` iteratee shorthand.
findLastIndex(users, 'active');
// => 0
```

Idea: Use the findLastIndex() method to return the index of the last element in the array that meets the provided test function conditions. If the corresponding element is not found, -1 is returned.

### head

Returns the first element in the array

```js
const head = arr => arr[0];

head([1, 2, 3]);
// => 1

head([]);
// => undefined
```

Idea: Directly return the first element of the array.

### flatten

Convert multidimensional array to one-dimensional array

```js
const flatten = arr => [].concat(...arr);

flatten([1, [2, [3, [4]], 5]]);
// => [1, 2, [3, [4]], 5]
```

Idea: Use the spread operator to expand a multi-dimensional array, and then use the concat method to splice the expanded one-dimensional arrays together to obtain the final one-dimensional array.

### flattenDeep

Convert a multi-dimensional array into a one-dimensional array recursively

```js
const flattenDeep = arr =>
   [].concat(...arr.map(v => (Array.isArray(v) ? flattenDeep(v) : v)));

flattenDeep([1, [2, [3, [4]], 5]]);
// => [1, 2, 3, 4, 5]
```

Idea: Use Array.map to traverse the array. For each element in the array, if it is an array, the flattenDeep function is called recursively, otherwise the element is returned directly. Finally, the spread operator is used to expand the array, and the concat method is used to splice it together.

### fromPairs

Convert a two-dimensional array to an object

```js
const fromPairs = arr =>
   arr.reduce((obj, [key, val]) => ({ ...obj, [key]: val }), {});

fromPairs([
   ['a', 1],
   ['b', 2],
]);
// => { 'a': 1, 'b': 2 }
```

Idea: Use Array.reduce to traverse the array. For each element in the array, deconstruct it into key and val, and add it to a new object. Finally, you will get an object containing all key-value pairs.

### indexOf

Returns the subscript of an element in the array, looking from front to back

```js
const indexOf = (arr, val, fromIndex = 0) =>
   arr.findIndex((item, index) => index >= fromIndex && item === val);

indexOf([1, 2, 1, 2], 2);
// => 1

// Search from the `fromIndex`.
indexOf([1, 2, 1, 2], 2, 2);
// => 3
```

Idea: Use the findIndex method of the array to search, and support searching starting from the specified index position.

### initial

Returns a new array with the last element removed from the original array

```js
const initial = arr => arr.slice(0, -1);

initial([1, 2, 3]);
// => [1, 2]
```

Idea: Use the slice method of the array to intercept the part except the last element and get a new array.

### intersection

Returns an array containing elements present in all arrays

```js
const intersection = (...arr) => [
   ...new Set(arr.reduce((a, b) => a.filter(v => b.includes(v)))),
];

intersection([2, 1], [4, 2], [1, 2]);
// => [2]
```

Idea: Use the reduce method of the array to traverse all arrays, use the filter method to filter out elements that also exist in the current array, and finally use Set to remove duplicates and convert them into an array.

### join

Convert an array to a string, separated by the specified delimiter

```js
const join = (arr, separator = ',') =>
   arr.reduce((res, val, i) => `${res}${i ? separator : ''}${val}`, '');

join(['a', 'b', 'c'], '~');
// => 'a~b~c'
```

Idea: Use the reduce method to traverse the array, concatenate each element and delimiter, and finally get a concatenated string.

### last

Returns the last element in the array

```js
const last = arr => arr[arr.length - 1];

last([1, 2, 3]);
// => 3
```

Idea: Return the last element in the array.

### lastIndexOf

Returns the subscript of an element in the array, looking from back to front

```js
const lastIndexOf = (arr, val) => arr.lastIndexOf(val);

lastIndexOf([1, 2, 1, 2], 2);
// => 3

// Search from the `fromIndex`.
lastIndexOf([1, 2, 1, 2], 2, 2);
// => 1
```

Idea: Use the lastIndexOf method of the array to find the subscript of the element in the array.

### pull

Remove the specified element from the array

```js
const pull = (arr, ...args) => arr.filter(item => !args.includes(item));

const array = [1, 2, 3, 1, 2, 3];

pull(array, 2, 3);
console.log(array);
// => [1, 1]
```

Idea: Use the filter method to filter out unnecessary elements.

### pullAt

Removes the element at the specified subscript from the array and returns a new array

```js
const pullAt = (arr, ...args) => args.map(index => arr.splice(index, 1)[0]);

const array = [5, 10, 15, 20];
const evens = pullAt(array, 1, 3);

console.log(array);
// => [5, 15]

console.log(evens);
// => [10, 20]
```

Idea: The map() method traverses the incoming subscript array, deletes the corresponding elements from the original array through the splice() method and returns it.

### reverse

Reverse an array

```js
const reverse = arr => [...arr].reverse();

const array = [1, 2, 3];

reverse(array);
// => [3, 2, 1]

console.log(array);
// => [3, 2, 1]
```

Idea: Just use destructuring assignment and reverse() method.

### slice

Returns a new array, intercepting the specified range of elements from the original array

```js
const slice = (arr, start, end) => arr.slice(start, end);
```

Idea: Call the native slice() method directly.

### sortedIndex

Returns the index into which an element should be inserted into the array

```js
const sortedIndex = (arr, value) => {
   let left = 0;
   let right = arr.length;
   while (left < right) {
     const mid = Math.floor((left + right) / 2);
     if (arr[mid] < value) {
       left = mid + 1;
     } else {
       right = mid;
     }
   }
   return right;
};

sortedIndex([30, 50], 40);
// => 1
```

Idea: Implement binary search algorithm to find the position where the element should be inserted.

### tail

Returns a new array, removing the first element from the original array

```js
const tail = arr => arr.slice(1);

tail([1, 2, 3]);
// => [2, 3]
```

Idea: Use the slice() method to remove the first element.

### take

Returns a new array containing the first n elements of the original array

```js
const take = (arr, n = 1) => arr.slice(0, n);

take([1, 2, 3]);
// => [1]

take([1, 2, 3], 2);
// => [1, 2]

take([1, 2, 3], 5);
// => [1, 2, 3]

take([1, 2, 3], 0);
// => []
```

Idea: Call the native slice() method directly, paying attention to the default parameters.

### takeRight

Returns a new array containing the last n elements of the original array

```js
const takeRight = (arr, n = 1) => arr.slice(-n);

takeRight([1, 2, 3]);
// => [3]

takeRight([1, 2, 3], 2);
// => [2, 3]

takeRight([1, 2, 3], 5);
// => [1, 2, 3]

takeRight([1, 2, 3], 0);
// => []
```

Idea: Also call the native slice() method directly, paying attention to the use of negative subscripts.

### union

Returns a new array containing all unique elements in the array

```js
const union = (...args) => [...new Set(args.flat())];

union([2], [1, 2]);
// => [2, 1]
```

Idea: The flat() method converts a multi-dimensional array into one dimension, the Set data structure removes duplication, and then converts it back to an array.

### uniq

Returns a new array containing all unique elements in the array

```js
const uniq = arr => [...new Set(arr)];

uniq([2, 1, 2]);
// => [2, 1]
```

Idea: Also use the Set() data structure to remove duplicates.

### without

Returns a new array, removing the specified elements from the original array

```js
const without = (arr, ...args) => arr.filter(item => !args.includes(item));

without([2, 1, 2, 3], 1, 2);
// => [3]
```

Idea: Same as pull method.

### xor

Returns a new array containing elements that appear in only one of the arrays

```js
const xor = (...args) =>
   args
     .flat()
     .filter(
       item => args.flat().indexOf(item) === args.flat().lastIndexOf(item)
     );

xor([2, 1], [2, 3]);
// => [1, 3]
```

Idea: The flat() method converts it into a one-dimensional array, and then uses the filter() method and the indexOf() and lastIndexOf() methods to determine the elements that only appear in one array.

###zip

Combine elements at the same position in multiple arrays into one array

```js
const zip = (...arrays) =>
   arrays[0].map((_, i) => arrays.map(array => array[i]));

zip(['fred', 'barney'], [30, 40], [true, false]);
// => [['fred', 30, true], ['barney', 40, false]]
```

Idea: Using the Rest parameter, first take out the first array, then use map to traverse the length of the first array, traverse the index elements of all arrays by index, combine them into a new array and return it.

### unzip

Restore the array generated by the zip function to the original array

```js
const unzip = array =>
   array.reduce(
     (acc, val) => (val.forEach((v, i) => acc[i].push(v)), acc),
     Array.from({ length: Math.max(...array.map(a => a.length)) }).map(() => [])
   );

const zipped = zip(['fred', 'barney'], [30, 40], [true, false]);
// => [['fred', 30, true], ['barney', 40, false]]

unzip(zipped);
// => [['fred', 'barney'], [30, 40], [true, false]]
```

Idea: Use reduce to traverse the array generated by the zip function, take out each value of each element, and form a new array based on the index to return. In the initial value of the reduce function, Math.max is used to obtain the maximum length of all elements, and Array.from is used to create a two-dimensional array of the corresponding length.

### dropWhile

Returns a new array, removing the elements from the beginning to the first element that meets the criteria in the original array

```js
const dropWhile = (array, predicate) =>
   array.slice(array.findIndex(val => !predicate(val)));

const users = [
   { user: 'barney', active: false },
   { user: 'fred', active: false },
   { user: 'pebbles', active: true },
];

dropWhile(users, o => !o.active);
// => objects for ['pebbles']

// The `matches` iteratee shorthand.
dropWhile(users, { user: 'barney', active: false });
// => objects for ['fred', 'pebbles']

// The `matchesProperty` iteratee shorthand.
dropWhile(users, ['active', false]);
// => objects for ['pebbles']

// The `property` iteratee shorthand.
dropWhile(users, 'active');
// => objects for ['barney', 'fred', 'pebbles']
```

Idea: Use the findIndex function to find the first element that does not meet the conditions, and then use the slice function to intercept the array after the element and return it.

### intersectionBy

Similar to intersection, but you can specify a function to compare elements in the array

```js
const intersectionBy = (arr1, arr2, compareFn) =>
   arr1.filter(item =>
     arr2.some(compareItem => compareFn(item) === compareFn(compareItem))
   );

intersectionBy([2.1, 1.2], [4.3, 2.4], Math.floor);
// => [2.1]

// The `property` iteratee shorthand.
intersectionBy([{ x: 1 }], [{ x: 2 }, { x: 1 }], 'x');
// => [{ 'x': 1 }]
```

Idea: Use the filter method of the array to traverse the first array, and use the function specified by the parameter to compare each element of the second array, and finally return the intersection of the two arrays.

### pullAll

Similar to pull, but takes an array as argument

```js
const pullAll = (arr, values) => arr.filter(item => !values.includes(item));

const array = [1, 2, 3, 1, 2, 3];

pullAll(array, [2, 3]);
console.log(array);
// => [1, 1]
```

Idea: Use the filter method of the array to traverse the original array and exclude elements that exist in the incoming array.

### pullAllBy

Similar to pullBy, but takes an array as parameter

```js
const pullAllBy = (arr, values, compareFn) =>
   arr.filter(
     item =>
       !values.some(compareItem => compareFn(item) === compareFn(compareItem))
   );

const array = [{ x: 1 }, { x: 2 }, { x: 3 }, { x: 1 }];

pullAllBy(array, [{ x: 1 }, { x: 3 }], 'x');
console.log(array);
// => [{ 'x': 2 }]
```

Idea: Use the filter method of the array to traverse the original array, exclude elements that exist in the passed array, and use the parameters
The function specified by the number compares the elements in two arrays.

### pullAllWith

Similar to pullWith, but takes an array as argument

```js
const pullAllWith = (arr, values, compareFn) =>
   arr.filter(item => !values.some(compareItem => compareFn(item, compareItem)));

const array = [
   { x: 1, y: 2 },
   { x: 3, y: 4 },
   { x: 5, y: 6 },
];

pullAllWith(array, [{ x: 3, y: 4 }], isEqual);
console.log(array);
// => [{ 'x': 1, 'y': 2 }, { 'x': 5, 'y': 6 }]
```

Idea: Use the filter method of the array to traverse the original array, exclude elements that exist in the incoming array, and use the function specified by the parameter to compare the elements in the two arrays.

### sortedIndexOf

Like indexOf, but can be used on sorted arrays

```js
const sortedIndexOf = (arr, value) => {
   let left = 0;
   let right = arr.length - 1;
   while (left <= right) {
     const mid = Math.floor((left + right) / 2);
     if (arr[mid] === value) {
       return mid;
     }
     if (arr[mid] < value) {
       left = mid + 1;
     } else {
       right = mid - 1;
     }
   }
   return -1;
};

sortedIndexOf([4, 5, 5, 5, 6], 5);
// => 1
```

Idea: Use the binary search algorithm to find the position of the specified element in the sorted array.

### sortedLastIndexOf

Like lastIndexOf, but can be used in sorted arrays

```js
const sortedLastIndexOf = (arr, value) => {
   let left = 0;
   let right = arr.length - 1;
   let lastIndex = -1;
   while (left <= right) {
     const mid = Math.floor((left + right) / 2);
     if (arr[mid] === value) {
       lastIndex = mid;
       left = mid + 1;
     } else if (arr[mid] < value) {
       left = mid + 1;
     } else {
       right = mid - 1;
     }
   }
   return lastIndex;
};

sortedLastIndex([4, 5, 5, 5, 6], 5);
// => 4
```

Idea: Use the binary search algorithm to find the last position of the specified element in the sorted array.

### sortedUniq

Like uniq, but can be used with sorted arrays

```js
const sortedUniq = arr =>
   arr.reduce((result, item) => {
     if (result.length === 0 || result[result.length - 1] !== item) {
       result.push(item);
     }
     return result;
   }, []);

sortedUniq([1, 1, 2]);
// => [1, 2]
```

Idea: Use the reduce method and indexOf method of the array to filter out duplicate elements and return a new array.

### sortedUniqBy

Like uniqBy, but can be used with sorted arrays

```js
const sortedUniqBy = (array, iteratee) =>
   array.reduce(
     (result, value) =>
       result.length && iteratee(value) === iteratee(result[result.length - 1])
         ?result
         : [...result, value],
     []
   );

sortedUniqBy([1.1, 1.2, 2.3, 2.4], Math.floor);
// => [1.1, 2.3]
```

Idea: Use the reduce method to traverse the original array, convert each element into a value through the specified function, and use the Set object to remove duplication, and finally return the deduplicated array.

### takeWhile

Returns a new array containing the elements from the beginning to the first element that does not meet the criteria in the original array

```js
const takeWhile = (array, predicate) =>
   array.slice(
     0,
     array.findIndex(element => !predicate(element))
   );
```

Idea: Use the findIndex method to find the index of the first element that does not meet the conditions, and then use the slice method to intercept the part of the original array that meets the conditions and return a new array.

### takeRightWhile

Returns a new array containing the elements from the last unqualified element to the end of the original array

```js
const takeRightWhile = (array, predicate) =>
   array
     .reverse()
     .slice(
       0,
       array.findIndex(element => !predicate(element))
     )
     .reverse();

const users = [
   { user: 'barney', active: true },
   { user: 'fred', active: false },
   { user: 'pebbles', active: false },
];

takeRightWhile(users, o => !o.active);
// => objects for ['fred', 'pebbles']

// The `matches` iteratee shorthand.
takeRightWhile(users, { user: 'pebbles', active: false });
// => objects for ['pebbles']

// The `matchesProperty` iteratee shorthand.
takeRightWhile(users, ['active', false]);
// => objects for ['fred', 'pebbles']

// The `property` iteratee shorthand.
takeRightWhile(users, 'active');
// => []
```

Idea: Use the reverse method to reverse the original array, then use the findIndex method to find the index of the first element in the reversed array that does not meet the conditions, and finally use the slice method to intercept the part from the index to the end of the original array and return the new array .

### unionBy

Similar to union, but you can specify a function to compare the elements in the array

```js
const unionBy = (...arrays) => {
   const iteratee = arrays.pop();
   const unionSet = new Set(
     arrays.reduce((result, array) => [...result, ...array], []).map(iteratee)
   );
   return Array.from(unionSet);
};

unionBy([2.1], [1.2, 2.3], Math.floor);
// => [2.1, 1.2]

// The `property` iteratee shorthand.
unionBy([{ x: 1 }], [{ x: 2 }, { x: 1 }], 'x');
// => [{ 'x': 1 }, { 'x': 2 }]
```

Idea: Use the Set object to deduplicate arrays, convert elements in multiple arrays into the same value by specifying functions, and finally convert the deduplicated values into arrays and return them.

### uniqBy

Similar to uniq, but you can specify a function to compare the elements in the array

```js
const uniqBy = (array, iteratee) => {
   const uniqSet = new Set(array.map(iteratee));
   return Array.from(uniqSet).map(value =>
     array.find(element => iteratee(element) === value)
   );
};

uniqBy([2.1, 1.2, 2.3], Math.floor);
// => [2.1, 1.2]

// The `property` iteratee shorthand.
uniqBy([{ x: 1 }, { x: 2 }, { x: 1 }], 'x');
// => [{ 'x': 1 }, { 'x': 2 }]
```

Idea: Similar to the implementation idea of unionBy method.

### unzipWith

Similar to unzip, but you can specify a function to process the array generated by the zip function

```js
const unzipWith = (array, iteratee) =>
   array.reduce(
     (result, value) => (
       value.forEach((innerValue, index) =>
         result[index].push(iteratee ? iteratee(innerValue) : innerValue)
       ),
       result
     ),
     Array.from({ length: Math.max(...array.map(value => value.length)) }).map(
       () => []
     )
   );

const zipped = zip([1, 2], [10, 20], [100, 200]);
// => [[1, 10, 100], [2, 20, 200]]

unzipWith(zipped, add);
// => [3, 30, 300]
```

Idea: Use the reduce method to traverse the incoming array, process each sub-array with the incoming function, store the processed value in the corresponding index position, and finally return the processed array.

### xorBy

Similar to xor, but you can specify a function to compare elements in the array

```js
const xorBy = (...arrays) => {
   const iteratee = arrays.pop();
   const valueCount = new Map();
   arrays.reduce((result, array) => [...result, ...array], [])
     .forEach((value) => {
       const newValue = iteratee(value);

xorBy([2.1, 1.2], [2.3, 3.4], Math.floor);
// => [1.2, 3.4]

// The `property` iteratee shorthand.
xorBy([{ 'x': 1 }], [{ 'x': 2 }, { 'x': 1 }], 'x');
// => [{ 'x': 2 }]
```

Idea: Use the reduce method to traverse the multiple incoming arrays, convert the elements in each array into specified values, use the Map object to count the number of occurrences of each value, and finally return the elements consisting of elements with the number of occurrences 1 array.

### zipObject

Convert two arrays into one object

```js
const zipObject = (keys, values) =>
   keys.reduce((obj, key, i) => ({ ...obj, [key]: values[i] }), {});

zipObject(['a', 'b'], [1, 2]);
// => { 'a': 1, 'b': 2 }
```

Idea: Use the reduce function to traverse the keys array, each time using an element in the keys array as the attribute name, and the elements at the same position in the values array as the attribute values, then store them in a new object, and finally return the object.

### zipObjectDeep

Convert two arrays into a nested object

```js
const zipObjectDeep = (keys, values) =>
   keys.reduce((obj, key, i) => {
     const path = key.split('.');
     const lastKey = path.pop();
     const nestedObj = path.reduceRight(
       (nested, prop) => ({ [prop]: nested }),
       values[i]
     );
     return mergeWith(obj, { [lastKey]: nestedObj }, customizer);
   }, {});

zipObjectDeep(['a.b[0].c', 'a.b[1].d'], [1, 2]);
// => { 'a': { 'b': [{ 'c': 1 }, { 'd': 2 }] } }
```

Idea: Use the reduce function to traverse the keys array, split an element in the keys array into a path array each time, use the reduceRight function to start traversing from the last element of the path array, and use this element as the attribute name each time, and the previous element The corresponding objects are used as property values, then they are stored in a new object, and finally the object is returned.

### gather

### countBy

Count the number of occurrences of each element in an array

```js
const countBy = (arr, fn) =>
   arr.map(typeof fn === 'function' ? fn : val => val[fn]).reduce((acc, val) => {
     acc[val] = (acc[val] || 0) + 1;
     return acc;
   }, {});

countBy([6.1, 4.2, 6.3], Math.floor);
// => { '4': 1, '6': 2 }

// The `property` iteratee shorthand.
countBy(['one', 'two', 'three'], 'length');
// => { '3': 2, '5': 1 }
```

Idea: Use map to map each element of the array. When fn is a function, execute it directly. Otherwise, take the corresponding attribute, and then use reduce to accumulate the number of occurrences of the element.

###each

Iterates over an array or object and performs the specified function on each element

```js
// each -> forEach
const each = (collection, fn) => {
   if (Array.isArray(collection)) {
     for (let i = 0; i < collection.length; i++) {
       fn(collection[i], i, collection);
     }
   } else {
     for (const key in collection) {
       if (collection.hasOwnProperty(key)) {
         fn(collection[key], key, collection);
       }
     }
   }
};

forEach([1, 2], value => {
   console.log(value);
});
// => Logs `1` then `2`.

forEach({ a: 1, b: 2 }, (value, key) => {
   console.log(key);
});
// => Logs 'a' then 'b' (iteration order is not guaranteed).
```

Idea: If it is an array, use a for loop to traverse and execute the fn function on each element; if it is an object, use a for-in loop to traverse and execute the fn function on each attribute.

### filter

Traverse an array or object and return elements that meet the criteria

```js
const filter = (collection, predicate) =>
   collection.filter(
     typeof predicate === 'function' ? predicate : val => val[predicate]
   );

const users = [
   { user: 'barney', age: 36, active: true },
   { user: 'fred', age: 40, active: false },
];

filter(users, o => !o.active);
// => objects for ['fred']

// The `matches` iteratee shorthand.
filter(users, { age: 36, active: true });
// => objects for ['barney']

// The `matchesProperty` iteratee shorthand.
filter(users, ['active', false]);
// => objects for ['fred']

// The `property` iteratee shorthand.
filter(users, 'active');
// => objects for ['barney']
```

Idea: Use the filter method to filter elements that meet the conditions. When predicate is a function, execute it directly. Otherwise, determine whether the element has the corresponding attribute value.

### find

Traverse an array or object and return the first element that meets the criteria

```js
const find = (collection, predicate) =>
   collection.filter(
     typeof predicate === 'function' ? predicate : val => val[predicate]
   )[0];

const users = [
   { user: 'barney', age: 36, active: true },
   { user: 'fred', age: 40, active: false },
   { user: 'pebbles', age: 1, active: true },
];

find(users, o => o.age < 40);
// => object for 'barney'

// The `matches` iteratee shorthand.
find(users, { age: 1, active: true });
// => object for 'pebbles'

// The `matchesProperty` iteratee shorthand.
find(users, ['active', false]);
// => object for 'fred'

// The `property` iteratee shorthand.
find(users, 'active');
// => object for 'barney'
```

Idea: Use the filter method to filter elements that meet the conditions and return the first element that meets the conditions. When predicate is a function, it is executed directly. Otherwise, it is judged whether the element has the corresponding attribute value.

### flatMap

Traverse the array, map each element into a new array, and then merge multiple arrays into a new array

```js
const flatMap = (arr, fn) => arr.reduce((acc, val) => acc.concat(fn(val)), []);

function duplicate(n) {
   return [n, n];
}

flatMap([1, 2], duplicate);
// => [1, 1, 2, 2]
```

Idea:

### flatMapDeep

Traverse the array, map each element into a new array, proceed recursively, and then merge multiple arrays into a new array

```js
const flatMapDeep = (arr, fn) =>
   arr.reduce(
     (acc, val) =>
       acc.concat(Array.isArray(val) ? flatMapDeep(val, fn) : fn(val)),
     []
   );

function duplicate(n) {
   return [[[n, n]]];
}

flatMapDeep([1, 2], duplicate);
// => [1, 1, 2, 2]
```

Idea: Use reduce to traverse the array, map each element into a new array, recursively if it is an array, and finally use concat to merge multiple arrays into a new array.

### flatMapDepth

Traverse the array, map each element into a new array, specify the recursion depth, and then merge multiple arrays into a new array

```js
const flatMapDepth = (array, iteratee, depth = 1) =>
   depth > 0
     ? array.reduce((acc, cur) => {
         const mapped = iteratee(cur);
         return acc.concat(
           Array.isArray(mapped)
             ? flatMapDepth(mapped, iteratee, depth - 1)
             : mapped
         );
       }, [])
     : array.slice();

function duplicate(n) {
   return [[[n, n]]];
}

flatMapDepth([1, 2], duplicate, 2);
// => [[1, 1], [2, 2]]
```

Idea: Use recursion to traverse the array depth-first, then map each element into a new array, and finally merge multiple arrays into a new array.

### forEach

Iterates over an array or object and performs the specified function on each element, similar to the each function

```js
const forEach = (collection, iteratee) => {
   for (const value of collection) {
     iteratee(value);
   }
};

_([1, 2]).forEach(value => {
   console.log(value);
});
// => Logs `1` then `2`.

forEach({ a: 1, b: 2 }, (value, key) => {
   console.log(key);
});
// => Logs 'a' then 'b' (iteration order is not guaranteed).
```

Idea: Use for...of to loop through the array or object and execute the specified function on each element.

### groupBy: Group the array in the specified way

```js
const groupBy = (array, iteratee) =>
   array.reduce((acc, cur) => {
     const key = typeof iteratee === 'function' ? iteratee(cur) : cur[iteratee];
     if (!acc[key]) {
       acc[key] = [];
     }
     acc[key].push(cur);
     return acc;
   }, {});

groupBy([6.1, 4.2, 6.3], Math.floor);
// => { '4': [4.2], '6': [6.1, 6.3] }

// The `property` iteratee shorthand.
groupBy(['one', 'two', 'three'], 'length');
// => { '3': ['one', 'two'], '5': ['three'] }
```

Idea: Use the reduce method to traverse the array and group the array in the specified way.

### includes

Determine whether an element is in an array or object

```js
const includes = (collection, value) => collection.includes(value);

includes([1, 2, 3], 1);
// => true

includes([1, 2, 3], 1, 2);
// => false

includes({ user: 'fred', age: 40 }, 'fred');
// => true

includes('pebbles', 'eb');
// => true
```

Idea: Use the Array.includes method to determine whether an element is in an array or object.

### invokeMap

Call the specified method on each element in the array and return the result

```js
const invokeMap = (array, path, ...args) =>
   array.map(value => {
     const method = typeof path === 'function' ? path : value[path];
     return method.apply(value, args);
   });

invokeMap(
   [
     [5, 1, 7],
     [3, 2, 1],
   ],
   'sort'
);
// => [[1, 5, 7], [1, 2, 3]]

invokeMap([123, 456], String.prototype.split, '');
// => [['1', '2', '3'], ['4', '5', '6']]
```

Idea: Use the Array.map method to call the specified method on each element in the array and return the result.

### keyBy

Convert the array into an object. The key value of the object is the value of the specified attribute, and the value is the element.

```js
const keyBy = (arr, key) =>
   arr.reduce((acc, val) => ((acc[val[key]] = val), acc), {});

const array = [
   { dir: 'left', code: 97 },
   { dir: 'right', code: 100 },
];

keyBy(array, o => String.fromCharCode(o.code));
// => { 'a': { 'dir': 'left', 'code': 97 }, 'd': { 'dir': 'right', 'code': 100 } }

keyBy(array, 'dir');
// => { 'left': { 'dir': 'left', 'code': 97 }, 'right': { 'dir': 'right', 'code': 100 } }
```

Idea: Use the reduce method to traverse the array and convert the array into an object. The key value of the object is the value of the specified attribute, and the value is the element.

### map: Traverse the array or object and map each element into a new element

```js
const map = (arr, func) => arr.reduce((acc, val) => [...acc, func(val)], []);

function square(n) {
   return n * n;
}

map([4, 8], square);
// => [16, 64]

map({ a: 4, b: 8 }, square);
// => [16, 64] (iteration order is not guaranteed)

const users = [{ user: 'barney' }, { user: 'fred' }];

// The `property` iteratee shorthand.
map(users, 'user');
// => ['barney', 'fred']
```

Idea: Use the reduce method to traverse the array and map each element into a new element.

### orderBy

Sort an array in a specified way

```js
const orderBy = (arr, props, orders) =>
   [...arr].sort((a, b) =>
     props.reduce((acc, prop, i) => {
       if (acc === 0) {
         const [p1, p2] =
           orders && orders[i] === 'desc'
             ? [b[prop], a[prop]]
             : [a[prop], b[prop]];
         acc = p1 > p2 ? 1 : p1 < p2 ? -1 : 0;
       }
       return acc;
     }, 0)
   );

const users = [
   { user: 'fred', age: 48 },
   { user: 'barney', age: 34 },
   { user: 'fred', age: 40 },
   { user: 'barney', age: 36 },
];

// Sort by `user` in ascending order and `age` in descending order.
orderBy(users, ['user', 'age'], ['asc', 'desc']);
// => objects for [['barney', 36], ['barney', 34], ['fred', 48], ['fred', 40]]
```

Idea: Use the sort method to sort the array. The props parameter represents the sorting properties, and the orders parameter represents the sorting order.

### partition

Split the array according to specified conditions

```js
const partition = (arr, func) =>
   arr.reduce((acc, val) => (acc[func(val) ? 0 : 1].push(val), acc), [[], []]);

const users = [
   { user: 'barney', age: 36, active: false },
   { user: 'fred', age: 40, active: true },
   { user: 'pebbles', age: 1, active: false },
];

partition(users, o => o.active);
// => objects for [['fred'], ['barney', 'pebbles']]

// The `matches` iteratee shorthand.
partition(users, { age: 1, active: false });
// => objects for [['pebbles'], ['barney', 'fred']]

// The `matchesProperty` iteratee shorthand.
partition(users, ['active', false]);
// => objects for [['barney', 'pebbles'], ['fred']]

// The `property` iteratee shorthand.
partition(users, 'active');
// => objects for [['fred'], ['barney', 'pebbles']]
```

Idea: Use the reduce method to traverse the array and split the array according to the specified conditions.

### reduce

Iterate over an array or object and add each element to the accumulator

```js
const reduce = (arr, func, initVal) => {
   let acc = initVal;
   for (let i = 0; i < arr.length; i++) {
     acc = func(acc, arr[i], i, arr);
   }
   return acc;
};

reduce([1, 2], (sum, n) => sum + n, 0);
// => 3

reduce(
   { a: 1, b: 2, c: 1 },
   (result, value, key) => {
     (result[value] || (result[value] = [])).push(key);
     return result;
   },
   {}
);
// => { '1': ['a', 'c'], '2': ['b'] } (The order of traversal cannot be guaranteed)
```

Idea: Use a for loop to traverse the array and add each element to the accumulator.

### reduceRight

Similar to reduce, but starts from the end of the array

```js
const reduceRight = (arr, func, initVal) => {
   let acc = initVal;
   for (let i = arr.length - 1; i >= 0; i--) {
     acc=func(acc, arr[i], i, arr);
   }
   return acc;
};

const array = [
   [0, 1],
   [twenty three],
   [4, 5],
];

reduceRight(array, (flattened, other) => flattened.concat(other), []);
// => [4, 5, 2, 3, 0, 1]
```

Idea: Similar to reduce, but starts traversing from the end of the array.

### reject

Traverse an array or object and return elements that do not meet the criteria

```js
const reject = (arr, fn) => arr.filter(x => !fn(x));

const users = [
   { user: 'barney', age: 36, active: false },
   { user: 'fred', age: 40, active: true },
];

reject(users, o => !o.active);
// => objects for ['fred']

// `matches` iteration abbreviation
reject(users, { age: 40, active: true });
// => objects for ['barney']

// `matchesProperty` shorthand for iteration
reject(users, ['active', false]);
// => objects for ['fred']

// `property` shorthand for iteration
reject(users, 'active');
// => objects for ['barney']
```

Idea: Use the filter method to traverse the array and return elements that do not meet the conditions.

### sample

Returns a random element from an array or object

```js
const sample = arr => arr[Math.floor(Math.random() * arr.length)];

sample([1, 2, 3, 4]);
// => 2
```

Idea: Use the Math.random method to generate a random number, and then randomly obtain an element based on the length of the array.

### sampleSize

Returns multiple elements in an array or object at random

```js
const sampleSize = (arr, n = 1) =>
   arr.sort(() => Math.random() - 0.5).slice(0, n);

sampleSize([1, 2, 3], 2);
// => [3, 1]

sampleSize([1, 2, 3], 4);
// => [2, 3, 1]
```

Idea: Use the sort method and Math.random method to disrupt the order of the array, and then use the slice method to intercept a specified number of elements.

###shuffle

Randomly shuffle elements in an array or object

```js
const shuffle = arr => arr.sort(() => Math.random() - 0.5);

shuffle([1, 2, 3, 4]);
// => [4, 1, 3, 2]
```

Idea: Use the sort method and Math.random method to disrupt the order of the array.

### size

Returns the length or number of elements of an array or object

```js
const size = obj => Object.keys(obj).length;

size([1, 2, 3]);
// => 3

size({ a: 1, b: 2 });
// => 2

size('pebbles');
// => 7
```

Idea: Use the Object.keys method to get the array of object properties, and then use the length property to get the length.

###some

Traverse the array or object to determine whether at least one element meets the conditions

```js
const some = (arr, fn) => arr.some(fn);

some([null, 0, 'yes', false], Boolean);
// => true

const users = [
   { user: 'barney', active: true },
   { user: 'fred', active: false },
];

// The `matches` iteratee shorthand.
some(users, { user: 'barney', active: false });
// => false

// The `matchesProperty` iteratee shorthand.
some(users, ['active', false]);
// => true

// The `property` iteratee shorthand.
some(users, 'active');
// => true
```

Idea: Use some method to determine whether at least one element meets the conditions.

### sortBy

Sort an array in a specified way

```js
const sortBy = (arr, fn) => arr.sort((a, b) => fn(a) - fn(b));

const users = [
   { user: 'fred', age: 48 },
   { user: 'barney', age: 36 },
   { user: 'fred', age: 40 },
   { user: 'barney', age: 34 },
];

sortBy(users, o => o.user);
// => objects for [['barney', 36], ['barney', 34], ['fred', 48], ['fred', 40]]

sortBy(users, ['user', 'age']);
// => objects for [['barney', 34], ['barney', 36], ['fred', 40], ['fred', 48]]

sortBy(users, 'user', o => Math.floor(o.age / 10));
// => objects for [['barney', 36], ['barney', 34], ['fred', 48], ['fred', 40]]
```

Idea: Use the sort method and the passed-in function to sort.

### Function

###after

Specifies how many times a function will be called before it is executed

```js
const after = (n, func) => {
   let count = 0;
   return (...args) => {
     count++;
     if (count >= n) {
       return func(...args);
     }
   };
};

const saves = ['profile', 'settings'];

const done = after(saves.length, () => {
   console.log('done saving!');
});

forEach(saves, type => {
   asyncSave({ type, complete: done });
});
// => Logs 'done saving!' after the two async saves have completed.
```

Idea: Return a function that executes the specified function after being called n times. Use closures to record the current number of calls and determine whether execution conditions are met.

### ary: Encapsulate the specified function and specify the maximum number of parameters it can receive.

```js
const ary =
   (func, n = func.length) =>
   (...args) =>
     func(...args.slice(0, n));

map(['6', '8', '10'], ary(parseInt, 1));
// => [6, 8, 10]
```

Idea: Encapsulate the specified function and specify it to receive up to n parameters. Returns a new function, limiting the number of function parameters.

### before: Specify how many times a function will be executed before being called.

```js
const before = (n, func) => {
   let count = 0;
   return (...args) => {
     count++;
     if (count < n) {
       return func(...args);
     }
   };
};

jQuery(element).on('click', before(5, addContactToList));
// => Allows adding up to 4 contacts to the list.
```

Idea: Return a function that executes the specified function before being called n times. Use closures to record the current number of calls and determine whether execution conditions are met.

### bind

Binds the function's this value to the specified parameters and returns a new function

```js
const bind =
   (func, thisArg, ...boundArgs) =>
   (...args) =>
     func.apply(thisArg, [...boundArgs, ...args]);

const greet = function (greeting, punctuation) {
   return `${greeting} ${this.user}${punctuation}`;
};

const object = { user: 'fred' };

var bound = bind(greet, object, 'hi');
bound('!');
// => 'hi fred!'

// Bound with placeholders.
var bound = bind(greet, object, _, '!');
bound('hi');
// => 'hi fred!'
```

Idea: Bind the this value of the function and the specified parameters, and return a new function. Implemented using apply and bind methods.

### bindKey

Similar to bind, but binds the specified method on the object

```js
const bindKey =
   (object, key, ...args) =>
   (...args2) =>
     object[key].apply(object, [...args, ...args2]);

const object = {
   user: 'fred',
   greet(greeting, punctuation) {
     return `${greeting} ${this.user}${punctuation}`;
   },
};

var bound = bindKey(object, 'greet', 'hi');
bound('!');
// => 'hi fred!'

object.greet = function (greeting, punctuation) {
   return `${greeting}ya ${this.user}${punctuation}`;
};

bound('!');
// => 'hiya fred!'

// Bound with placeholders.
var bound = bindKey(object, 'greet', _, '!');
bound('hi');
// => 'hiya fred!'
```

Idea: Similar to bind, but it binds the specified method on the object. Implemented using apply and bind methods.

### curry

Curry the specified function

```js
const curry = (func, arity = func.length, ...args) =>
   arity <= args.length ? func(...args) : curry.bind(null, func, arity, ...args);

const abc = function (a, b, c) {
   return [a, b, c];
};

const curried = curry(abc);

curried(1)(2)(3);
// => [1, 2, 3]

curried(1, 2)(3);
// => [1, 2, 3]

curried(1, 2, 3);
// => [1, 2, 3]

// Curried with placeholders.
curried(1)(_, 3)(2);
// => [1, 2, 3]
```

Idea: Curry the specified function. Return a new function. When the number of parameters is insufficient, continue to return a new function until the number of parameters is sufficient to execute the original function.

### curryRight

Like curry, but handles arguments from right to left

```js
const curryRight = (func, arity = func.length, ...args) =>
   arity <= args.length
     ? func(...args.reverse())
     : curryRight.bind(null, func, arity, ...args);

const abc = function (a, b, c) {
   return [a, b, c];
};

const curried = curryRight(abc);

curried(3)(2)(1);
// => [1, 2, 3]

curried(2, 3)(1);
// => [1, 2, 3]

curried(1, 2, 3);
// => [1, 2, 3]

// Curried with placeholders.
curried(3)(1, _)(2);
// => [1, 2, 3]
```

Idea: Similar to curry, but parameters are processed from right to left.

### debounce

Perform anti-shake processing on the specified function

```js
const debounce = (func, wait, immediate = false) => {
   let timeoutId;
   return (...args) => {
     if (immediate && !timeoutId) {
       func(...args);
     }
     clearTimeout(timeoutId);
     timeoutId = setTimeout(() => {
       if (!immediate) {
         func(...args);
       }
       timeoutId = null;
     }, wait);
   };
};

// Avoid expensive calculation overhead when the window changes.
jQuery(window).on('resize', debounce(calculateLayout, 150));

// `sendMail` is then called when clicked.
jQuery(element).on(
   'click',
   debounce(sendMail, 300, {
     leading: true,
     trailing: false,
   })
);

// Ensure that `batchLog` will be triggered within 1 second after calling it once.
const debounced = debounce(batchLog, 250, { maxWait: 1000 });
const source = new EventSource('/stream');
jQuery(source).on('message', debounced);

// Cancel a trailing anti-shake call
jQuery(window).on('popstate', debounced.cancel);
```

Idea: Perform anti-shake processing on the specified function. Returns a new function that is executed only once for a period of time.

### defer

Delay the execution of the specified function

```js
const defer = (func, ...args) => setTimeout(func, 1, ...args);

defer(text => {
   console.log(text);
}, 'deferred');
// => Output 'deferred' for one millisecond or more.
```

Idea: Delay the execution of the specified function. Implemented using setTimeout.

### delay

Delay the execution of the specified function for a period of time

```js
const delay = (func, wait, ...args) => setTimeout(func, wait, ...args);

delay(
   text => {
     console.log(text);
   },
   1000,
   'later'
);
// => Output 'later' after one second.
```

Idea: Delay the execution of the specified function for a period of time. Implemented using setTimeout.

### flip

Reverse the parameters of the specified function

```js
const flip =
   fn =>
   (...args) =>
     fn(...args.reverse());

const flipped = flip(function () {
   return toArray(arguments);
});

flipped('a', 'b', 'c', 'd');
// => ['d', 'c', 'b', 'a']
```

Idea: Reverse the order of parameters of the function and return a new function

### memoize

Memorize the specified function and cache the calculation results of the function

```js
const memoize = fn => {
   const cache = new Map();
   return (...args) => {
     const key = JSON.stringify(args);
     return cache.has(key)
       ? cache.get(key)
       : cache.set(key, fn(...args)).get(key);
   };
};

const object = { a: 1, b: 2 };
const other = { c: 3, d: 4 };

var values = memoize(values);
values(object);
// => [1, 2]

values(other);
// => [3, 4]

object.a = 2;
values(object);
// => [1, 2]

// Modify the result cache.
values.cache.set(object, ['a', 'b']);
values(object);
// => ['a', 'b']

// Replace `memoize.Cache`.
memoize.Cache = WeakMap;
```

Idea: cache the calculation results of the function and return a new function

### negate

Encapsulate the specified function and return the negative value of the original function

```js
const negate =
   fn =>
   (...args) =>
     !fn(...args);

function isEven(n) {
   return n % 2 == 0;
}

filter([1, 2, 3, 4, 5, 6], negate(isEven));
// => [1, 3, 5]
```

Idea: Return a new function that inverts the result of executing the original function

### once

Specifies that a function can only be called once

```js
const once = fn => {
   let called = false;
   return (...args) => {
     if (!called) {
       called = true;
       return fn(...args);
     }
   };
};

const initialize = once(createApplication);
initialize();
initialize();
// `initialize` can only call `createApplication` once.
```

Idea: Return a new function, which can only be called once

### overArgs

Encapsulate the specified function and convert the form of the parameters

```js
const overArgs =
   (fn, transforms) =>
   (...args) =>
     fn(...args.map((arg, index) => transforms[index](arg)));

function doubled(n) {
   return n * 2;
}

function square(n) {
   return n * n;
}

const func = overArgs((x, y) => [x, y], [square, doubled]);

func(9, 3);
// => [81, 6]

func(10, 5);
// => [100, 10]
```

Idea: Return a new function that converts the specified parameters of the original function

### partial

Partially apply the specified function and specify some parameters

```js
const partial =
   (fn, ...args) =>
   (...newArgs) =>
     fn(...args, ...newArgs);

const greet = function (greeting, name) {
   return `${greeting} ${name}`;
};

const sayHelloTo = partial(greet, 'hello');
sayHelloTo('fred');
// => 'hello fred'

// Placeholders are used.
const greetFred = partial(greet, _, 'fred');
greetFred('hi');
// => 'hi fred'
```

Idea: Return a new function that partially applies the specified parameters of the original function

### partialRight

Similar to partial, but partial arguments are specified from right to left

```js
const partialRight =
   (fn, ...args) =>
   (...newArgs) =>
     fn(...newArgs, ...args);

const greet = function (greeting, name) {
   return `${greeting} ${name}`;
};

const greetFred = partialRight(greet, 'fred');
greetFred('hi');
// => 'hi fred'

// Placeholders are used.
const sayHelloTo = partialRight(greet, 'hello', _);
sayHelloTo('fred');
// => 'hello fred'
```

Idea: Return a new function that starts from Apply the specified parameters of the original function from right to left

### rearg

Encapsulate the specified function and adjust the position of the parameters

```js
const rearg =
   (fn, indexes) =>
   (...args) =>
     fn(...indexes.map(index => args[index]));

const rearged = rearg((a, b, c) => [a, b, c], [2, 0, 1]);

rearged('b', 'c', 'a');
// => ['a', 'b', 'c']
```

Idea: Return a new function that adjusts the parameter order of the original function

### rest

Encapsulate the specified function, collect the parameters into an array and pass it into the original function

```js
const rest =
   fn =>
   (...args) =>
     fn(args);

const say = rest(
   (what, names) =>
     `${what} ${initial(names).join(', ')}${size(names) > 1 ? ', & ' : ''}${last(
       names
     )}`
);

say('hello', 'fred', 'barney', 'pebbles');
// => 'hello fred, barney, & pebbles'
```

Idea: Return a new function that collects the parameters of the original function into an array and passes it in

### spread

Encapsulate the specified function, expand the parameter array and pass it into the original function as multiple parameters.

```js
const spread = fn => args => fn(...args);

const say = spread((who, what) => `${who} says ${what}`);

say(['fred', 'hello']);
// => 'fred says hello'

const numbers = Promise.all([Promise.resolve(40), Promise.resolve(36)]);

numbers.then(spread((x, y) => x + y));
// => a Promise of 76
```

Idea: Return a new function that expands the parameter array of the original function and passes it in as multiple parameters

### throttle

Throttling the specified function

```js
const throttle = (fn, time) => {
   let timer;
   return (...args) => {
     if (!timer) {
       timer = setTimeout(() => {
         fn(...args);
         timer = null;
       }, time);
     }
   };
};

// Avoid excessive updating of positioning during scrolling
jQuery(window).on('scroll', throttle(updatePosition, 100));

// `renewToken` is called after clicking, but more than once within 5 minutes.
const throttled = throttle(renewToken, 300000, { trailing: false });
jQuery(element).on('click', throttled);

// Cancel a trailing throttling call.
jQuery(window).on('popstate', throttled.cancel);
```

Idea: Return a new function that throttles the original function

### Object

### assign

Merge the properties of objects, and the properties of subsequent objects will overwrite the properties of previous objects.

```js
const assign = (...objs) =>
   objs.reduce((result, obj) => Object.assign(result, obj), {});

function Foo() {
   this.a = 1;
}

function Bar() {
   this.c = 3;
}

Foo.prototype.b = 2;
Bar.prototype.d = 4;

assign({ a: 0 }, new Foo(), new Bar());
// => { 'a': 1, 'c': 3 }
```

Idea: Use the reduce method to traverse each object and merge the attributes into the target object.

### defaults

Encapsulate the specified object and merge the default value into it

```js
const defaults = (obj, defaultProps) => ({ ...defaultProps, ...obj });

defaults({ a: 1 }, { b: 2 }, { a: 3 });
// => { 'a': 1, 'b': 2 }
```

Idea: Use the Object.assign method to merge the default value object into the target object. If the same attribute already exists in the target object, it will not be overwritten.

### defaultsDeep

Similar to defaults, but supports nested objects

```js
const defaultsDeep = (obj, defaultProps) => {
   const mergeDeep = (target, source) => {
     Object.keys(source).forEach(key => {
       const targetValue = target[key];
       const sourceValue = source[key];
       if (typeof targetValue === 'object' && typeof sourceValue === 'object') {
         target[key] = mergeDeep(targetValue, sourceValue);
       } else {
         target[key] = targetValue === undefined ? sourceValue : targetValue;
       }
     });
     return target;
   };
   return mergeDeep({ ...defaultProps }, obj);
};

defaultsDeep({ a: { b: 2 } }, { a: { b: 1, c: 3 } });
// => { 'a': { 'b': 2, 'c': 3 } }
```

Idea: Use the Object.assign and typeof methods to perform recursive traversal and merge nested objects into them.

### findKey

Traverse the object and return the first key name that meets the conditions

```js
const findKey = (obj, predicate) => {
   for (const key in obj) {
     if (predicate(obj[key], key, obj)) {
       return key;
     }
   }
};

const users = {
   barney: { age: 36, active: true },
   fred: { age: 40, active: false },
   pebbles: { age: 1, active: true },
};

findKey(users, o => o.age < 40);
// => 'barney' (iteration order is not guaranteed)

// The `matches` iteratee shorthand.
findKey(users, { age: 1, active: true });
// => 'pebbles'

// The `matchesProperty` iteratee shorthand.
findKey(users, ['active', false]);
// => 'fred'

// The `property` iteratee shorthand.
findKey(users, 'active');
// => 'barney'
```

Idea: Use for...in to loop through the object, call the specified function for each attribute to judge, and return the current attribute name if a true value is returned.

### findLastKey

Similar to findKey, but starts at the end of the object

```js
const findLastKey = (obj, predicate) =>
   findKey(obj, predicate, Object.keys(obj).reverse());

const users = {
   barney: { age: 36, active: true },
   fred: { age: 40, active: false },
   pebbles: { age: 1, active: true },
};

findLastKey(users, o => o.age < 40);
// => returns 'pebbles' assuming `findKey` returns 'barney'

// The `matches` iteratee shorthand.
findLastKey(users, { age: 36, active: true });
// => 'barney'

// The `matchesProperty` iteratee shorthand.
findLastKey(users, ['active', false]);
// => 'fred'

// The `property` iteratee shorthand.
findLastKey(users, 'active');
// => 'pebbles'
```

Idea: Use the Object.keys method to get the key array of the object, then use the reverse method to flip the array, and then use the findKey function to search.

### forIn

Traverse the object and call the specified function for each attribute

```js
const forIn = (obj, iteratee) => {
   for (const key in obj) {
     if (iteratee(obj[key], key, obj) === false) {
       break;
     }
   }
   return obj;
};

function Foo() {
   this.a = 1;
   this.b = 2;
}

Foo.prototype.c = 3;

forIn(new Foo(), (value, key) => {
   console.log(key);
});
// => Logs 'a', 'b', then 'c' (the order of traversal cannot be guaranteed).
```

Idea: Use for...in to loop through the object and call the specified function for each attribute.

### forInRight

Similar to forIn, but starts traversing from the end of the object

```js
const forInRight = (obj, fn) => {
   for (const key in obj) {
     if (obj.hasOwnProperty(key)) {
       fn(obj[key], key, obj);
     }
   }
};

function Foo() {
   this.a = 1;
   this.b = 2;
}

Foo.prototype.c = 3;

forInRight(new Foo(), (value, key) => {
   console.log(key);
});
// => print 'c', 'b', then 'a', `forIn` will print 'a', 'b', then 'c'.
```

Idea: Use a for...in loop to traverse all the properties of the object in reverse order and call the specified function for each property.

### forOwn

Traverse the enumerable properties of the object itself and call the specified function for each property

```js
const forOwn = (obj, func) => {
   for (const key in obj) {
     if (obj.hasOwnProperty(key)) {
       func(obj[key], key, obj);
     }
   }
};

function Foo() {
   this.a = 1;
   this.b = 2;
}

Foo.prototype.c = 3;

forOwn(new Foo(), (value, key) => {
   console.log(key);
});
// => Output 'a' then 'b' (the order of traversal is not guaranteed).
```

Idea: Traverse the enumerable properties of the object itself, call the specified function for each property, use a for-in loop to traverse all the properties of the object, determine whether the property is its own enumerable property, and if so, call the specified function.

### forOwnRight

Similar to forOwn, but starts traversing from the end of the object

```js
const forOwnRight = (obj, func) => {
   const keys = Object.keys(obj).reverse();
   for (let i = 0; i < keys.length; i++) {
     func(obj[keys[i]], keys[i], obj);
   }
};

function Foo() {
   this.a = 1;
   this.b = 2;
}

Foo.prototype.c = 3;

forOwnRight(new Foo(), (value, key) => {
   console.log(key);
});
// => print 'b' then 'a', `forOwn` will print 'a' then 'b'
```

Idea: Similar to forOwn, but starting from the end of the object, you can reverse the key array of the object and then traverse.

### functions

Returns all function names on the specified object

```js
const functions = obj =>
   Object.keys(obj).filter(key => typeof obj[key] === 'function');

function Foo() {
   this.a = constant('a');
   this.b = constant('b');
}

Foo.prototype.c = constant('c');

functions(new Foo());
// => ['a', 'b']
```

Idea: Return all function names on the specified object, use Object.keys() to obtain all attribute names of the object, and then use the filter() method to filter out attribute names whose attribute values are of type function.

###get

Get the attributes on the object and support using dots and square brackets to specify the attribute path.

```js
const get = (obj, path) =>
   path.split(/[.[\]]/).reduce((acc, cur) => (cur ? acc[cur] : acc), obj);

const object = { a: [{ b: { c: 3 } }] };

get(object, 'a[0].b.c');
// => 3

get(object, ['a', '0', 'b', 'c']);
// => 3

get(object, 'a.b.c', 'default');
// => 'default'
```

Idea: Use the reduce function to split the attribute path and then traverse and obtain the corresponding attribute value. It supports specifying the attribute path using dots and square brackets.

### has

Determine whether there is a specified attribute on the object

```js
const has = (obj, key) => key in obj;

const object = { a: { b: 2 } };
const other = create({ a: create({ b: 2 }) });

has(object, 'a');
// => true

has(object, 'a.b');
// => true

has(object, ['a', 'b']);
// => true

has(other, 'a');
// => false
```

Idea: Use the in operator to determine whether there is a specified attribute on the object

### hasIn

Determine whether the object has an attribute with the specified path

```js
const hasIn = (obj, path) => get(obj, path) !== undefined;

const object = create({ a: create({ b: 2 }) });

hasIn(object, 'a');
// => true

hasIn(object, 'a.b');
// => true

hasIn(object, ['a', 'b']);
// => true

hasIn(object, 'b');
// => false
```

Idea: Use the get function to obtain the attribute value. If undefined is returned, it means that the specified path attribute does not exist.

### invert

Reverse the properties and values of the specified object

```js
const invert = obj =>
   Object.entries(obj).reduce((acc, [key, val]) => {
     acc[val] = key;
     return acc;
   }, {});

const object = { a: 1, b: 2, c: 1 };

invert(object);
// => { '1': 'c', '2': 'b' }
```

Idea: Traverse the object and use the attribute value as the key name, and the attribute name as the key value to generate a new object

### invertBy

Similar to invert, but supports specifying a collection of inverted values

```js
const invertBy = (obj, fn) =>
   Object.entries(obj).reduce((acc, [key, val]) => {
     const invertedKey = fn(val);
     if (!acc[invertedKey]) {
       acc[invertedKey] = [];
     }
     acc[invertedKey].push(key);
     return acc;
   }, {});

const object = { a: 1, b: 2, c: 1 };

invertBy(object);
// => { '1': ['a', 'c'], '2': ['b'] }

invertBy(object, value => `group${value}`);
// => { 'group1': ['a', 'c'], 'group2': ['b'] }
```

Idea: Traverse the object and process the attribute value through the callback function as the key name, and use the attribute name as the key value to generate a new object

### invoke

Call a method on the specified object

```js
const invoke = (obj, methodName, ...args) =>
   Object.values(obj).forEach(func =>
     typeof func[methodName] === 'function' ? func[methodName](...args) : null
   );

const object = { a: [{ b: { c: [1, 2, 3, 4] } }] };

invoke(object, 'a[0].b.c.slice', 1, 3);
// => [2, 3]
```

Idea: Traverse the object and call the method with the specified method name

### keys

Returns all enumerable property names on the object

```js
const keys = obj => Object.keys(obj);

function Foo() {
   this.a = 1;
   this.b = 2;
}

Foo.prototype.c = 3;

keys(new Foo());
// => ['a', 'b'] (iteration order is not guaranteed)

keys('hi');
// => ['0', '1']
```

Idea: Use the Object.keys function to return all enumerable property names on the object

### keysIn

Returns all property names on the object, including non-enumerable properties

```js
const keysIn = obj => {
   const result = [];
   for (const key in obj) {
     result.push(key);
   }
   return result;
};

function Foo() {
   this.a = 1;
   this.b = 2;
}

Foo.prototype.c = 3;

keysIn(new Foo());
// => ['a', 'b', 'c'] (iteration order is not guaranteed)
```

Idea: Traverse all property names of the object, add them to an array, and return the array.

### mapKeys

Iterates over each property on the object, returning a new object where the name of each property is calculated by the specified function

```js
const mapKeys = (obj, fn) =>
   Object.keys(obj).reduce((result, key) => {
     result[fn(obj[key], key, obj)] = obj[key];
     return result;
   }, {});

mapKeys({ a: 1, b: 2 }, (value, key) => key + value);
// => { 'a1': 1, 'b2': 2 }
```

Idea: Use reduce to traverse the attribute names of the object, calculate the new attribute name through the specified function, add it to a new object together with the original attribute value, and return the new object.

### mapValues

Iterates over each property on the object, returning a new object in which the value of each property is calculated by the specified function

```js
const mapValues = (obj, fn) =>
   Object.keys(obj).reduce((result, key) => {
     result[key] = fn(obj[key], key, obj);
     return result;
   }, {});

const users = {
   fred: { user: 'fred', age: 40 },
   pebbles: { user: 'pebbles', age: 1 },
};

mapValues(users, o => o.age);
// => { 'fred': 40, 'pebbles': 1 } (iteration order is not guaranteed)

// The `property` iteratee shorthand.
mapValues(users, 'age');
// => { 'fred': 40, 'pebbles': 1 } (iteration order is not guaranteed)
```

Idea: Use reduce to traverse the attribute names of the object, calculate each attribute value through the specified function, add the calculated new attribute value to a new object, and return the new object.

### merge

Merges the properties of the object and source object and returns the merged object

```js
const merge = (obj, src) => ({ ...obj, ...src });

const object = {
   a: [{ b: 2 }, { d: 4 }],
};

const other = {
   a: [{ c: 3 }, { e: 5 }],
};

merge(object, other);
// => { 'a': [{ 'b': 2, 'c': 3 }, { 'd': 4, 'e': 5 }] }
```

Idea: Use Object.assign to merge the attribute values of the source object into the target object, and return the merged new object.

### mergeWith

Similar to merge, but specifies a merge function to handle conflicting attribute values

```js
const mergeWith = (obj, src, customizer) => {
   const result = { ...obj, ...src };
   Object.keys(result).forEach(key => {
     result[key] = customizer(obj[key], src[key], key, obj, src);
   });
   return result;
};

function customizer(objValue, srcValue) {
   if (isArray(objValue)) {
     return objValue.concat(srcValue);
   }
}

const object = { a: [1], b: [2] };
const other = { a: [3], b: [4] };

mergeWith(object, other, customizer);
// => { 'a': [1, 3], 'b': [2, 4] }
```

Idea: Use Object.assign to merge the attribute values of the source object into the target object, traverse the merged new object, customize the conflicting attribute values by specifying a function, and return the processed new object.

### omit

Returns a new object with the property values of the specified properties omitted

```js
const omit = (obj, props) => {
   const newObj = { ...obj };
   props.forEach(prop => {
     delete newObj[prop];
   });
   return newObj;
};

const object = { a: 1, b: '2', c: 3 };

omit(object, ['a', 'c']);
// => { 'b': '2' }
```

Idea: Use Object.assign to copy the attribute values of the original object to a new object, traverse the specified omitted attributes, delete them from the new object, and return the new object.

### omitBy

Similar to omit, but determines whether to omit attributes based on the specified function

```js
const omitBy = (obj, predicate) => {
   const newObj = { ...obj };
   Object.keys(newObj).forEach(key => {
     if (predicate(newObj[key])) {
       delete newObj[key];
     }
   });
   return newObj;
};

const object = { a: 1, b: '2', c: 3 };

omitBy(object, isNumber);
// => { 'b': '2' }
```

Idea: Use Object.assign to copy the attribute values of the original object to a new object, traverse each attribute of the new object, determine whether the attribute needs to be deleted according to the specified function, and return the processed new object.

### pick

Returns a new object containing only the property values of the specified property

```js
const pick = (obj, props) =>
   props.reduce((result, prop) => {
     if (prop in obj) {
       result[prop] = obj[prop];
     }
     return result;
   }, {});

const object = { a: 1, b: '2', c: 3 };

pick(object, ['a', 'c']);
// => { 'a': 1, 'c': 3 }
```

Idea: Use reduce to traverse the specified attributes that need to be selected, add them to a new object, and return the new object.

### pickBy

Similar to pick, but determines whether to retain attributes based on the specified function

```js
const pickBy = (obj, fn) =>
   Object.keys(obj).reduce((acc, key) => {
     if (fn(obj[key])) acc[key] = obj[key];
     return acc;
   }, {});

const object = { a: 1, b: '2', c: 3 };

pickBy(object, isNumber);
// => { 'a': 1, 'c': 3 }
```

Idea: Use the Object.keys and Array.prototype.reduce methods to return a new object.

### result

Get the value of the specified path on the object and make function calls according to the situation

```js
const result = (obj, path, defaultValue) =>
   path.split('.').reduce((acc, cur) => (acc ? acc[cur] : undefined), obj) ??
   defaultValue;

const object = { a: [{ b: { c1: 3, c2: constant(4) } }] };

result(object, 'a[0].b.c1');
// => 3

result(object, 'a[0].b.c2');
// => 4

result(object, 'a[0].b.c3', 'default');
// => 'default'

result(object, 'a[0].b.c3', constant('default'));
// => 'default'
```

Idea: Use the Array.prototype.reduce method and typeof operator to support obtaining the value of multi-layer paths.

### set

Sets the property value of the specified path on the object

```js
const set = (obj, path, value) => {
   const keys = path.split(/[,[\].]+?/);
   const lastKeyIndex = keys.length - 1;
   keys.reduce((acc, key, index) => {
     if (index === lastKeyIndex) acc[key] = value;
     else acc[key] ?? (acc[key] = {});
     return acc[key];
   }, obj);
   return obj;
};

const object = { a: [{ b: { c: 3 } }] };

set(object, 'a[0].b.c', 4);
console.log(object.a[0].b.c);
// => 4

set(object, ['x', '0', 'y', 'z'], 5);
console.log(object.x[0].y.z);
// => 5
```

Idea: Use the Array.prototype.reduce method to support setting the value of multi-layer paths.

### setWith

Similar to set, but specifies a custom function to use to set the property value

```js
const setWith = (obj, path, value, customizer) => {
   const keys = path.split(/[,[\].]+?/);
   const lastKeyIndex = keys.length - 1;
   keys.reduce((acc, key, index) => {
     const newValue = index === lastKeyIndex ? customizer(acc[key], value) : {};
     acc[key] = typeof acc[key] === 'object' ? acc[key] : newValue;
     return acc[key];
   }, obj);
   return obj;
};

const object = {};

setWith(object, '[0][1]', 'a', Object);
// => { '0': { '1': 'a' } }
```

Idea: Use the Array.prototype.reduce method to support setting the value of multi-layer paths.

### toPairs

Convert object to array of key-value pairs

```js
const toPairs = obj => Object.entries(obj);

function Foo() {
   this.a = 1;
   this.b = 2;
}

Foo.prototype.c = 3;

toPairs(new Foo());
// => [['a', 1], ['b', 2]] (iteration order is not guaranteed)
```

Idea: Use the Object.entries method to return an array composed of key-value pairs.

### toPairsIn

Convert an object into an array of key-value pairs, including non-enumerable properties

```js
const toPairsIn = obj => {
   const result = [];
   for (const key in obj) {
     result.push([key, obj[key]]);
   }
   return result;
};

function Foo() {
   this.a = 1;
   this.b = 2;
}

Foo.prototype.c = 3;

toPairsIn(new Foo());
// => [['a', 1], ['b', 2], ['c', 3]] (iteration order is not guaranteed)
```

Idea: Use the Object.getOwnPropertyNames method to return an array composed of key-value pairs.

### transform

Encapsulate the specified object, specify the conversion function, and process the attributes on the object

```js
const transform = (obj, fn, acc) =>
   Object.entries(obj).reduce(
     (result, [key, value]) => fn(result, value, key, obj),
     acc
   );

transform(
   [2, 3, 4],
   (result, n) => {
     result.push((n *= n));
     return n % 2 == 0;
   },
   []
);
// => [4, 9]

transform(
   { a: 1, b: 2, c: 1 },
   (result, value, key) => {
     (result[value] || (result[value] = [])).push(key);
   },
   {}
);
// => { '1': ['a', 'c'], '2': ['b'] }
```

Idea: Use the Object.entries method and the Array.prototype.reduce method to return an array composed of converted objects.

### unset

Delete the attribute value of the specified path on the object

```js
```

Idea:

### update: Get the value of the specified path on the object, make function calls according to the situation, and finally set the value back

```js
```

Idea:

### updateWith: similar to update, but specifies a custom function to update attribute values

```js
```

Idea:

### values: Returns all enumerable property values on the object

```js
```

Idea:

### valuesIn: Returns all attribute values on the object, including non-enumerable attribute values

```js
```

Idea:

```js
```

Idea:

### gather

### countBy: Traverse the collection and return an object, where the key is the value calculated by the specified function, and the value is the number of times the value appears.

### each: Traverse the collection and call the specified function on each element

```js
```

Idea:

### eachRight: Similar to each, but starts traversing from the end of the collection

```js
```

Idea:

### every: Traverse the collection and return a Boolean value indicating whether all elements satisfy the specified function

```js
```

Idea:

### filter: Traverse the collection and return a new collection that only contains elements that satisfy the specified function

```js
```

Idea:

### find: Traverse the collection and return the first element that satisfies the specified function

```js
```

Idea:

### findLast: Similar to find, but starts traversing from the end of the collection

```js
```

Idea:

### flatMap: Traverse the collection, call the specified function on each element, and flatten the result into a new collection

```js
```

Idea:

### flatMapDeep: Similar to flatMap, but the result of calling the iterator on each element is flattened deeply.

```js
```

Idea:

### flatMapDepth: similar to flatMap, but you can specify the flattening depth

```js
```

Idea:

### forEach: Traverse the collection and call the specified function on each element

```js
```

Idea:

### forEachRight: Similar to forEach, but starts traversing from the end of the collection

```js
```

Idea:

### groupBy: Traverse the collection and return an object, where the key is the value calculated by the specified function, and the value is the set of elements corresponding to the value.

```js
```

Idea:

### includes: Determine whether the specified element is included in the collection

```js
```

Idea:

### invokeMap: Call the specified method on each element in the collection and return the result

```js
```

Idea:

### keyBy: Traverse the collection and return an object in which the key is calculated by the specified function and the value is the element

```js
```

Idea:

### map: Traverse the collection and return a new collection in which each element is calculated by the specified function

```js
```

Idea:

### orderBy: Sort the collection in the specified order

```js
```

Idea:

### partition: Traverse the collection and return an array containing a collection of elements that satisfy and do not satisfy the specified function.

```js
```

Idea:

### reduce: Traverse the collection, accumulate the collection elements, and return the final result

```js
```

Idea:

### reduceRight: Similar to reduce, but starts traversing from the end of the collection

```js
```

Idea:

### reject: Traverse the collection and return a new collection that does not contain elements that satisfy the specified function

```js
```

Idea:

### sample: Returns a random element in the collection

```js
```

Idea:

### sampleSize: Returns a specified number of random elements in the collection

```js
```

Idea:

### shuffle: Returns a randomly arranged collection

```js
```

Idea:

### size: Returns the size of the collection

```js
```

Idea:

### some: Traverse the collection and return a Boolean value indicating whether any element satisfies the specified function

```js
```

Idea:

### sortBy: Traverse the collection, call the specified function on each element in the specified order, and return the result

```js
```

Idea:

### toArray: Convert the collection into an array

```js
```

Idea:

### toPairs: Convert the collection into an array of key-value pairs

```js
```

Idea:

### union: merge multiple collections into one collection and remove duplicates at the same time

```js
```

Idea:

### uniq: Returns a new collection that does not contain duplicate elements

```js
```

Idea:

### uniqBy: Similar to uniq, but deduplicates according to the specified function

```js
```

Idea:

### uniqWith: Similar to uniq, but uses the specified comparison function for deduplication

```js
```

Idea:

### unzip: Return the grouped elements to the structure before packaging

```js
```

Idea:

### unzipWith: Similar to unzip, but uses the specified function to merge each group element

```js
```

Idea:

### without: Returns a new collection that does not contain the specified element

```js
```

Idea:

### xor: Returns a new set containing elements that appear in only one of the sets

```js
```

Idea:

### zip: Merge multiple collections into a new collection of elements. Each element is composed of elements at the corresponding position of each original collection.

```js
```

Idea:

### zipObject: Combine key array and value array into a new object

```js
```

Idea:

### zipWith: Similar to zip, but uses the specified function to merge each group element

```js
```

Idea:

## Ramda

> A practical JavaScript functional programming library

### Ramda function list

### JavaScript Advanced Programming 4th Edition Study Notes

## Common React interview questions

### React interview questions

| Serial Number | Question |
| -- | -------------------------------------------------- -------------------------------- |
| 1 | [What does the key attribute do? ](#key-What is the function of the attribute) |
| 2 | [What does the refs attribute do? ](#refs-What does the attribute do) |
| 3 | [What does the PureComponent component do? ](#purecomponent-What does the component do) |
| 4 | [What does the memo method do? ](#memo-What does the method do) |
| 5 | [What does the error boundary do? ](#What is the role of error boundary) |
| 6 | [What are controlled components and uncontrolled components? ](#What are controlled components and uncontrolled components) |
| 7 | [What are higher-order components? ](#What is a high-order component) |
| 8 | [What are the life cycle methods and what is their execution order? ](#What are the life cycle methods and what is their execution order) |
| 9 | [What does the getDerivedStateFromProps life cycle method do? ](#getderivedstatefromprops-What is the role of life cycle methods) |
| 10 | [What does the shouldComponentUpdate life cycle method do? ](#shouldcomponentupdate-What does the life cycle method do) |
| 11 | [What does the getSnapshotBeforeUpdate life cycle method do? ](#getsnapshotbeforeupdate-What does the life cycle method do) |
| 12 | [What is React context? ](#What is-react-context) |
| 13 | [What is useState in React Hook? ](What is usestate- in #react-hook-) |

1. ### What is the function of key attribute?

The `key` attribute is used to identify each child element in the list, allowing for more efficient updates to the DOM when elements are added, moved, or removed. The `key` attribute should be a unique string, preferably generated based on the unique identifier of the element in the list.

```jsx
function App() {
   const items = [
     { id: 1, name: 'foo' },
     { id: 2, name: 'bar' },
   ];

   return (
     <ul>
       {items.map(item => (
         <li key={item.id}>{item.name}</li>
       ))}
     </ul>
   );
}
```

2. ### What is the role of the refs attribute?

     The `refs` attribute is used to reference components or DOM elements. It can be used in function components and class components.

```jsx
function App() {
   const buttonRef = useRef();

   function handleClick() {
     buttonRef.current.disabled = true;
   }

   return (
     <>
       <button ref={buttonRef} onClick={handleClick}>
         Click me
       </button>
     </>
   );
}
```

3. ### What does the PureComponent component do?

     `PureComponent` is an optimized version based on Component that uses shallow comparison in the shouldComponentUpdate life cycle method to determine whether re-rendering is needed. `PureComponent` will not re-render if all props and state have not changed.

```jsx
class MyComponent extends React.PureComponent {
   render() {
     return <div>Hello, {this.props.name}!</div>;
   }
}
```

4. ### What does the memo method do?

The `memo` method is a higher-order component used for shallow comparison optimization of function components. It takes a function component and returns a new component that will use the previous result if the props haven't changed.

```jsx
function MyComponent(props) {
   return <div>Hello, {props.name}!</div>;
}

const MemoizedComponent = React.memo(MyComponent);
```

5. ### What is the role of error boundaries?

ErrorBoundary is a React component used to catch and handle JavaScript errors in child components. Error boundaries catch errors that occur during rendering, but not errors in event handlers, asynchronous code, and server-side rendering.

```jsx
class ErrorBoundary extends React.Component {
   constructor(props) {
     super(props);
     this.state = { hasError: false };
   }

   static getDerivedStateFromError(error) {
     return { hasError: true };
   }

   componentDidCatch(error, info) {
     console.error(error, info);
   }

   render() {
     if (this.state.hasError) {
     return <div>Something went wrong.</div>;
   }

   return this.props.children;
}

function App() {
   return (
     <ErrorBoundary>
       <MyComponent />
     </ErrorBoundary>
   );
}
```

6. ### What are controlled components and uncontrolled components?

     In React, form elements such as input, textarea, and select are usually divided into controlled and uncontrolled components.

`Controlled components` means that the value of a form element is controlled by the state of a React component. As the user enters content, React updates the component's state, thus updating the form element's value in real time. At this point, the values of the form elements are maintained by React and have nothing to do with the DOM itself. Controlled components typically need to implement an onChange event handler to update the component's state as the user enters content.

Here is a sample code using a controlled component:

```jsx
class Input extends React.Component {
   constructor(props) {
     super(props);
     this.state = {
       value: '',
     };
   }

   handleChange = event => {
     this.setState({
       value: event.target.value,
     });
   };

   render() {
     return (
       <input
         type="text"
         value={this.state.value}
         onChange={this.handleChange}
       />
     );
   }
}
```

In the above code, we define an Input component as a controlled component, and its value is controlled by the component's state value. When the user enters content, the onChange event handler function handleChange will be called to update the state of the component, thereby updating the value of the form element in real time.

In contrast, an `uncontrolled component` means that the form element's value is maintained by the DOM itself and is not controlled by the React component. When the user enters content, the value of the form element is updated directly to the DOM without any processing in React. At this time, the component cannot directly obtain the value of the form element, but needs to obtain it through ref.

Here is a sample code using an uncontrolled component:

```jsx
class Input extends React.Component {
   constructor(props) {
     super(props);
     this.inputRef = React.createRef();
   }

   handleClick = () => {
     console.log(this.inputRef.current.value);
   };

   render() {
     return (
       <div>
         <input type="text" ref={this.inputRef} />
         <button onClick={this.handleClick}>Click</button>
       </div>
     );
   }
}
```

In the above code, we have defined an Input component as an uncontrolled component whose value is maintained by the DOM itself. When the user clicks the button, we can get the value of the form element through ref for subsequent processing.

7. ### What are higher-order components?

     `Higher-Order Component` (HOC for short) refers to a function that accepts a component as a parameter and returns a new component. HOC is essentially a way of component reuse, used to enhance the functionality of components or encapsulate some common logic to achieve code reuse.

Here is a sample code using higher-order components:

```jsx
function withLogger(WrappedComponent) {
   return class extends React.Component {
     componentDidMount() {
       console.log(`Component ${WrappedComponent.name} mounted`);
     }

     render() {
       return <WrappedComponent {...this.props} />;
     }
   };
}

class MyComponent extends React.Component {
   render() {
     return <div>Hello, world!</div>;
   }
}

const EnhancedComponent = withLogger(MyComponent);

ReactDOM.render(<EnhancedComponent />, document.getElementById('root'));
```

In the above code, we define a higher-order component withLogger, which accepts a component as a parameter and returns a new component. The new component will output the component name when mounted, and then render the incoming component.

We defined a MyComponent component and enhanced it using the withLogger higher-order component to get a new enhanced component EnhancedComponent. Finally, we render the EnhancedComponent into the DOM.

In the above example, the withLogger high-order component is used to record the mounting information of the component to make it more convenient during development and debugging. When we need to perform similar operations on multiple components, we can use the withLogger higher-order component without writing the same mounting logic in each component. In this way, we can achieve code reuse while making the code more concise and easy to understand.

8. ### What are the life cycle methods and what is their execution order?

     Lifecycle methods in React can be divided into three categories: mount, update, and unmount. Their execution order is as follows:

* Mount:
     * constructor
     * getDerivedStateFromProps
     *render
     * componentDidMount
*   renew:
     * getDerivedStateFromProps
     * shouldComponentUpdate
     *render
     * getSnapshotBeforeUpdate
     * componentDidUpdate
*   uninstall:
     * componentWillUnmount

Among them, the `getDerivedStateFromProps` life cycle method is a static method used to update the state of the component when props change. It should return an object to update the state, or null to indicate no update is required.

```jsx
class MyComponent extends React.Component {
   static getDerivedStateFromProps(props, state) {
     if (props.value !== state.value) {
       return { value: props.value };
     }
     return null;
   }

   constructor(props) {
     super(props);
     this.state = { value: props.value };
   }

   render() {
     return <div>{this.state.value}</div>;
   }
}
```

Secondly, the `shouldComponentUpdate` lifecycle method is used to decide whether the component needs to be re-rendered when props or state change. It should return a boolean value indicating whether the component needs to be updated. By default, `shouldComponentUpdate` returns true.

```jsx
class MyComponent extends React.Component {
   shouldComponentUpdate(nextProps, nextState) {
     return this.props.value !== nextProps.value;
   }

   render() {
     return <div>{this.props.value}</div>;
   }
}
```

Finally, the `getSnapshotBeforeUpdate` lifecycle method is called before the component is updated, which can be used to capture some information before the DOM is updated. It should return a value as the third parameter of the componentDidUpdate method.

```jsx
class MyComponent extends React.Component {
   constructor(props) {
     super(props);
     this.listRef = React.createRef();
   }

   getSnapshotBeforeUpdate(prevProps, prevState) {
     if (prevProps.items.length < this.props.items.length) {
       const list = this.listRef.current;
       return list.scrollHeight - list.scrollTop;
     }
     return null;
   }

   componentDidUpdate(prevProps, prevState, snapshot) {
     if (snapshot !== null) {
       const list = this.listRef.current;
       list.scrollTop = list.scrollHeight - snapshot;
     }
   }

   render() {
     return (
       <div ref={this.listRef}>
         {this.props.items.map(item => (
           <div key={item.id}>{item.name}</div>
         ))}
       </div>
     );
   }
}
```

9. ### What does the getDerivedStateFromProps life cycle method do?

`getDerivedStateFromProps` is a static lifecycle method of React components.
Its main function is to allow the component to synchronously update the component's state when it receives new props.

This method will be called by React in the following two situations:
After component instantiation and before rendering

Before receiving new `props`, whether caused by the parent component or the `props` changes obtained through the external API
The `getDerivedStateFromProps` method receives two parameters:

`props`: latest `props`

`state`: current `state`

This method should return an object to update `state`, or `null` to not update any state.

```jsx
class MyComponent extends React.Component {
   static getDerivedStateFromProps(props, state) {
     // You can update state based on changes in props
     if (props.value !== state.value) {
       return {
         value: props.value,
       };
     }

     // Return null and do not update state
     return null;
   }
}
```

`Key points`:
`getDerivedStateFromProps` is a pure function and should not have side effects, such as making network requests or subscriptions.

This method is not recommended for frequent use as it may cause the code to become complex and difficult to maintain.

In many scenarios, you can use other lifecycle methods or React's newly introduced `Hooks`.

When changes in props need to be mapped to `state`, you can consider using it, but in many cases, the rendering content can be calculated directly from `props` without using `state`.

This method was introduced since React `v16.3` and `componentWillReceiveProps` has been deprecated because it is safer and will not be affected by future asynchronous rendering features. In future versions of React, the use of class-based lifecycle methods will gradually give way to functional components using `Hooks`.

10. ### What does the shouldComponentUpdate life cycle method do?

`shouldComponentUpdate` is a life cycle method of React class components. Its main function is to determine whether the output of a component needs to be updated. That is, when the `props` or `state` of the component changes, the `shouldComponentUpdate` method will be called before rendering is executed. Called to indicate to React whether it should continue the rendering process.

This method returns `true` by default, which means that the component will be re-rendered every time the state changes. However, by returning `false`, you can prevent unnecessary rendering of components, which can improve the performance of your application, especially when the component tree is very large or computationally intensive.

`shouldComponentUpdate` receives two parameters:

`nextProps`: new `props` to be received

`nextState`: the new `state` to be updated

```jsx
class MyComponent extends React.Component {
   shouldComponentUpdate(nextProps, nextState) {
     // Compare the old and new props or state and only update the component when they actually change
     return (
       nextProps.someValue !== this.props.someValue ||
       nextState.someState !== this.state.someState
     );
   }
}
```

`Note`:

It is only called during component update and will not be called during first rendering.

If `false` is returned, the component will not perform an update operation, the `render` method will not be called, and the rendering of child components will also be skipped.
It should not produce any side effects and should be a pure function.

In most cases, you don't need to write the `shouldComponentUpdate` method manually. If you need to optimize performance, it is recommended to use `React.PureComponent`, which already implements a shallow comparison similar to `shouldComponentUpdate`.

Starting with React `16.3` version, a new "lifecycle" API has been introduced. If you are using the new lifecycle methods, or plan to migrate to functional components and `Hooks`, `shouldComponentUpdate` may become less common

In particular, `React.memo` works similar to PureComponent for functional components, providing similar performance improvements.

11. ### What does the getSnapshotBeforeUpdate life cycle method do?

`getSnapshotBeforeUpdate` is a lifecycle method in React class components that allows you to capture certain information about the component (for example, the scroll position) before the latest rendered output is submitted to the `DOM`.

This lifecycle method is called before new rendering output is drawn, and it can return a value or `null`. If the returned value is not `null`, the returned value will be passed as the third parameter to `componentDidUpdate`.

This mechanism is particularly useful because sometimes after updating the `DOM` you may need to adjust the scroll position based on the previous state, or perform similar operations to keep the user's view state unchanged.

`getSnapshotBeforeUpdate` receives two parameters:

`prevProps`: `props` before update

`prevState`: `state` before update

If your component doesn't use getSnapshotBeforeUpdate, you don't need to implement it; only use it if you really need to capture some information before updating and apply it after updating.

```jsx
class MyComponent extends React.Component {
   getSnapshotBeforeUpdate(prevProps, prevState) {
     // Check whether prevProps or prevState meets specific conditions
     // For example, you can capture the old scroll position:
     if (prevProps.list.length < this.props.list.length) {
       const list = document.getElementById('list');
       return list.scrollHeight - list.scrollTop;
     }
     return null;
   }

   componentDidUpdate(prevProps, prevState, snapshot) {
     // If the `snapshot` returned by `getSnapshotBeforeUpdate` is not `null`
     // You can do some things with `snapshot`
     if (snapshot !== null) {
       const list = document.getElementById('list');
       list.scrollTop = list.scrollHeight - snapshot;
     }
   }
}
```

In the above example, `getSnapshotBeforeUpdate` is used to capture the scroll position before adding a new item to the list, and then use this snapshot information (`snapshot`) through `componentDidUpdate` to adjust the scroll bar to maintain the scroll position relative to the bottom. Does not change even if the list length changes.

It should be noted that `getSnapshotBeforeUpdate` and `componentDidUpdate`, when used together, can handle operations that need to be performed immediately after the DOM is updated.

12. ### What is React context?

     React context is a way to share data across component levels, avoiding the trouble of passing data through props. It consists of two parts: `Provider` and `Consumer`.

`Provider` is a component that accepts a value attribute, which represents shared data. The `Consumer` of the subcomponent that wraps it can access this data.

```jsx
const MyContext = React.createContext();

class MyComponent extends React.Component {
   render() {
     return (
       <MyContext.Provider value={42}>
         <ChildComponent />
       </MyContext.Provider>
     );
   }
}

function ChildComponent() {
   return (
     <MyContext.Consumer>
       {value => <div>The answer is {value}.</div>}
     </MyContext.Consumer>
   );
}
```

13. ### What is useState in React Hook?

useState is a type of React Hook that allows us to use state in functional components.

```jsx
import React, { useState } from 'react';

function Counter() {
   const [count, setCount] = useState(0);
   return (
     <div>
       <h1>{count}</h1>
       <button onClick={() => setCount(count + 1)}>Increment</button>
     </div>
   );
}
```

## 🐠 Hydrology in previous issues

[Implement a react component that automatically calculates width and height based on ResizeObserver](https://dev.to/niexq/implement-a-react-component-that-automatically-calculates-width-and-height-based-on-resizeobserver-3fko)