Lodash 函数列表 · 对象
assign
合并对象的属性,后面的对象的属性会覆盖前面的对象
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 }
思路:使用 reduce 方法遍历每个对象,将属性合并到目标对象中。
defaults
对指定对象进行封装,将默认值合并进去
const defaults = (obj, defaultProps) => ({ ...defaultProps, ...obj });
defaults({ a: 1 }, { b: 2 }, { a: 3 });
// => { 'a': 1, 'b': 2 }
思路:使用 Object.assign 方法将默认值对象合并到目标对象上,如果目标对象中已经存在相同属性,则不会被覆盖。
defaultsDeep
与 defaults 类似,但是支持嵌套对象
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 } }
思路:使用 Object.assign 和 typeof 方法进行递归遍历,将嵌套的对象也合并进去。
findKey
遍历对象,返回第一个符合条件的键名
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'
思路:使用 for...in 循环遍历对象,对每个属性调用指定的函数进行判断,如果返回真值则返回当前属性名。
findLastKey
与 findKey 类似,但是从对象的末尾开始
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'
思路:使用 Object.keys 方法获取对象的键名数组,然后使用 reverse 方法翻转数组,再使用 findKey 函数进行查找。
forIn
遍历对象,对每个属性调用指定的函数
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' (无法保证遍历的顺序)。
思路:使用 for...in 循环遍历对象,对每个属性调用指定的函数。
forInRight
与 forIn 类似,但是从对象的末尾开始遍历
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);
});
// => 输出 'c', 'b', 然后 'a', `forIn` 会输出 'a', 'b', 然后 'c'。
思路:使用 for...in 循环倒序遍历对象的所有属性,并对每个属性调用指定的函数。
forOwn
遍历对象自身的可枚举属性,对每个属性调用指定的函数
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);
});
// => 输出 'a' 然后 'b' (无法保证遍历的顺序)。
思路:遍历对象自身的可枚举属性,对每个属性调用指定的函数,使用 for-in 循环遍历对象的所有属性,判断属性是否是自身的可枚举属性,如果是则调用指定的函数。
forOwnRight
与 forOwn 类似,但是从对象的末尾开始遍历
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);
});
// => 输出 'b' 然后 'a', `forOwn` 会输出 'a' 然后 'b'
思路:与 forOwn 类似,但是从对象的末尾开始遍历,可以将对象的键数组进行 reverse 操作后再遍历。
functions
返回指定对象上的所有函数名
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']
思路:返回指定对象上的所有函数名,使用 Object.keys()获取对象的所有属性名,再使用 filter()方法筛选出属性值的类型为 function 的属性名。
get
获取对象上的属性,支持使用点和方括号的方式指定属性路径
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'
思路:使用 reduce 函数将属性路径分割后进行遍历并获取对应属性值,支持使用点和方括号的方式指定属性路径
has
判断对象上是否有指定属性
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
思路:使用 in 操作符判断对象上是否有指定属性
hasIn
判断对象上是否有指定路径的属性
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
思路:使用 get 函数获取属性值,如果返回 undefined 则表示不存在指定路径属性
invert
对指定对象的属性和值进行反转
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' }
思路:遍历对象并将属性值作为键名,属性名作为键值生成新对象
invertBy
与 invert 类似,但是支持指定反转后值的集合
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'] }
思路:遍历对象并将属性值经过回调函数处理后作为键名,属性名作为键值生成新对象
invoke
对指定对象上的方法进行调用
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]
思路:遍历对象并调用指定方法名的方法
keys
返回对象上的所有可枚举属性名
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']
思路:使用 Object.keys 函数返回对象上的所有可枚举属性名
keysIn
返回对象上的所有属性名,包括不可枚举属性
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)
思路:遍历对象的所有属性名,将其添加到一个数组中,并返回该数组。
mapKeys
遍历对象上的每个属性,返回一个新对象,其中每个属性的名称由指定的函数计算得出
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 }
思路:使用 reduce 遍历对象的属性名,将新的属性名通过指定函数计算得出,并与原属性值一起添加到一个新的对象中,并返回该新对象。
mapValues
遍历对象上的每个属性,返回一个新对象,其中每个属性的值由指定的函数计算得出
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)
思路:使用 reduce 遍历对象的属性名,通过指定函数计算每个属性值,并将计算后的新属性值添加到一个新的对象中,并返回该新对象。
merge
合并对象和源对象的属性,并返回合并后的对象
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 }] }
思路:使用 Object.assign 将源对象的属性值合并到目标对象上,并返回合并后的新对象。
mergeWith
与 merge 类似,但是指定合并函数,用于处理冲突的属性值
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] }
思路:使用 Object.assign 将源对象的属性值合并到目标对象上,并遍历合并后的新对象,通过指定函数自定义处理冲突的属性值,并返回处理后的新对象。
omit
返回一个新对象,其中省略了指定属性的属性值
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' }
思路:使用 Object.assign 将原对象的属性值复制到一个新对象上,遍历指定省略的属性,将其从新对象中删除,并返回该新对象。
omitBy
与 omit 类似,但是根据指定函数判断是否省略属性
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' }
思路:使用 Object.assign 将原对象的属性值复制到一个新对象上,遍历新对象的每个属性,根据指定函数判断是否需要删除该属性,并返回处理后的新对象。
pick
返回一个新对象,其中只包含指定属性的属性值
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 }
思路:使用 reduce 遍历指定需要选取的属性,将其添加到一个新的对象中,并返回该新对象。
pickBy
与 pick 类似,但是根据指定函数判断是否保留属性
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 }
思路:使用 Object.keys 和 Array.prototype.reduce 方法,返回一个新的对象。
result
获取对象上指定路径的值,并根据情况进行函数调用
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'
思路:使用 Array.prototype.reduce 方法和 typeof 运算符,支持获取多层路径的值。
set
设置对象上指定路径的属性值
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
思路:使用 Array.prototype.reduce 方法,支持设置多层路径的值。
setWith
与 set 类似,但是指定自定义函数用于设置属性值
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' } }
思路:使用 Array.prototype.reduce 方法,支持设置多层路径的值。
toPairs
将对象转化为键值对数组
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)
思路:使用 Object.entries 方法,返回一个由键值对组成的数组。
toPairsIn
将对象转化为键值对数组,包括不可枚举属性
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)
思路:使用 Object.getOwnPropertyNames 方法,返回一个由键值对组成的数组。
transform
对指定对象进行封装,指定转换函数,处理对象上的属性
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'] }
思路:使用 Object.entries 方法和 Array.prototype.reduce 方法,返回一个由转换后的对象组成的数组。
unset
删除对象上指定路径的属性值
const unset = (obj, path) => {
const keys = Array.isArray(path) ? path : path.split(/[,[\].]+?/);
const lastKeyIndex = keys.length - 1;
keys.reduce((acc, key, index) => {
if (index === lastKeyIndex) {
delete acc[key];
}
return acc[key];
}, obj);
return obj;
};
const object = { a: [{ b: { c: 7 } }] };
unset(object, 'a[0].b.c');
// => true
console.log(object);
// => { 'a': [{ 'b': {} }] }
unset(object, ['a', '0', 'b', 'c']);
// => true
思路:使用 reduce 方法遍历路径数组,在最后一个键时删除对应属性,返回修改后的对象。
update
获取对象上指定路径的值,并根据情况进行函数调用,最后将值设置回去
const update = (obj, path, updater) => {
const keys = Array.isArray(path) ? path : path.split(/[,[\].]+?/);
const lastKeyIndex = keys.length - 1;
keys.reduce((acc, key, index) => {
if (index === lastKeyIndex) {
acc[key] = updater(acc[key]);
} else {
acc[key] = acc[key] || {};
}
return acc[key];
}, obj);
return obj;
};
const object = { a: [{ b: { c: 3 } }] };
update(object, 'a[0].b.c', n => n * n);
console.log(object.a[0].b.c);
// => 9
update(object, 'x[0].y.z', n => (n || 0) + 1);
console.log(object.x[0].y.z);
// => 1
思路:使用 reduce 方法遍历路径,在最后一个键时应用更新函数,如果路径不存在则创建。
updateWith
与 update 类似,但是指定自定义函数用于更新属性值
const updateWith = (obj, path, updater, customizer) => {
const keys = Array.isArray(path) ? path : path.split(/[,[\].]+?/);
const lastKeyIndex = keys.length - 1;
keys.reduce((acc, key, index) => {
if (index === lastKeyIndex) {
acc[key] = updater(acc[key]);
} else {
acc[key] = acc[key] || customizer(acc[key], key, acc);
}
return acc[key];
}, obj);
return obj;
};
const object = {};
updateWith(object, '[0][1]', constant('a'), Object);
// => { '0': { '1': 'a' } }
思路:与 update 类似,但使用自定义函数来创建中间对象。
values
返回对象上的所有可枚举属性值
const values = obj => Object.values(obj);
function Foo() {
this.a = 1;
this.b = 2;
}
Foo.prototype.c = 3;
values(new Foo());
// => [1, 2] (iteration order is not guaranteed)
values('hi');
// => ['h', 'i']
思路:使用 Object.values 方法返回对象的所有可枚举属性值。
valuesIn
返回对象上的所有属性值,包括不可枚举属性值
const valuesIn = obj => {
const result = [];
for (const key in obj) {
result.push(obj[key]);
}
return result;
};
function Foo() {
this.a = 1;
this.b = 2;
}
Foo.prototype.c = 3;
valuesIn(new Foo());
// => [1, 2, 3] (iteration order is not guaranteed)
思路:使用 for...in 循环遍历对象的所有属性(包括继承的),将属性值添加到结果数组中。
countBy
统计数组中每个元素出现的次数
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 }
思路:使用 map 将数组每个元素进行映射,fn 是函数时直接执行,否则取对应属性,再使用 reduce 将元素出现的次数进行累加
each
遍历数组或对象,并对每个元素执行指定的函数
// 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).
思路:如果是数组,则使用 for 循环遍历,对每个元素执行 fn 函数;如果是对象,则使用 for-in 循环遍历,对每个属性执行 fn 函数
filter
遍历数组或对象,返回符合条件的元素
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']
思路:使用 filter 方法过滤符合条件的元素,predicate 是函数时直接执行,否则判断元素是否具有对应的属性值
find
遍历数组或对象,返回第一个符合条件的元素
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'
思路:使用 filter 方法过滤符合条件的元素,并返回第一个符合条件的元素,predicate 是函数时直接执行,否则判断元素是否具有对应的属性值
flatMap
遍历数组,将每个元素映射成一个新的数组,再将多个数组合并成一个新数组
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]
思路:使用 reduce 方法遍历数组,将每个元素映射成一个新的数组,然后使用 concat 将多个数组合并成一个新数组。
flatMapDeep
遍历数组,将每个元素映射成一个新的数组,递归进行,再将多个数组合并成一个新数组
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]
思路:使用 reduce 遍历数组,将每个元素映射成一个新的数组,如果是数组则递归进行,最后使用 concat 将多个数组合并成一个新数组
flatMapDepth
遍历数组,将每个元素映射成一个新的数组,指定递归的深度,再将多个数组合并成一个新数组
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]]
思路:使用递归的方式实现深度优先遍历数组,然后将每个元素映射成一个新的数组,最后将多个数组合并成一个新数组。
forEach
遍历数组或对象,并对每个元素执行指定的函数,与 each 函数类似
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).
思路:使用 for...of 循环遍历数组或对象,并对每个元素执行指定的函数。
groupBy:按照指定的方式对数组进行分组
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'] }
思路:使用 reduce 方法遍历数组,按照指定的方式对数组进行分组。
includes
判断一个元素是否在数组或对象中
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
思路:使用 Array.includes 方法判断一个元素是否在数组或对象中。
invokeMap
对数组中的每个元素调用指定的方法,并返回结果
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']]
思路:使用 Array.map 方法对数组中的每个元素调用指定的方法,并返回结果。
keyBy
将数组转化为对象,对象的键值是指定属性的值,值是该元素
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 } }
思路:使用 reduce 方法遍历数组,将数组转化为对象,对象的键值是指定属性的值,值是该元素。
map:遍历数组或对象,将每个元素映射成一个新的元素
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']
思路:使用 reduce 方法遍历数组,将每个元素映射成一个新的元素。
orderBy
按照指定的方式对数组进行排序
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 },
];
// 以 `user` 升序排序 再 `age` 以降序排序。
orderBy(users, ['user', 'age'], ['asc', 'desc']);
// => objects for [['barney', 36], ['barney', 34], ['fred', 48], ['fred', 40]]
思路:使用 sort 方法对数组进行排序,props 参数表示排序的属性,orders 参数表示排序的顺序。
partition
按照指定的条件对数组进行分割
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']]
思路:使用 reduce 方法遍历数组,按照指定的条件对数组进行分割。
reduce
遍历数组或对象,累加每个元素到累加器中
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'] } (无法保证遍历的顺序)
思路:使用 for 循环遍历数组,累加每个元素到累加器中。
reduceRight
与 reduce 类似,但是从数组的末尾开始遍历
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],
[2, 3],
[4, 5],
];
reduceRight(array, (flattened, other) => flattened.concat(other), []);
// => [4, 5, 2, 3, 0, 1]
思路:与 reduce 类似,但是从数组的末尾开始遍历。
reject
遍历数组或对象,返回不符合条件的元素
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` 迭代简写
reject(users, { age: 40, active: true });
// => objects for ['barney']
// `matchesProperty` 迭代简写
reject(users, ['active', false]);
// => objects for ['fred']
// `property` 迭代简写
reject(users, 'active');
// => objects for ['barney']
思路:使用 filter 方法遍历数组,返回不符合条件的元素即可。
sample
随机返回数组或对象中的一个元素
const sample = arr => arr[Math.floor(Math.random() * arr.length)];
sample([1, 2, 3, 4]);
// => 2
思路:使用 Math.random 方法生成随机数,再根据数组的长度随机获取一个元素。
sampleSize
随机返回数组或对象中的多个元素
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]
思路:使用 sort 方法和 Math.random 方法打乱数组顺序,再使用 slice 方法截取指定数量的元素。
shuffle
随机打乱数组或对象中的元素
const shuffle = arr => arr.sort(() => Math.random() - 0.5);
shuffle([1, 2, 3, 4]);
// => [4, 1, 3, 2]
思路:使用 sort 方法和 Math.random 方法打乱数组顺序即可。
size
返回数组或对象的长度或元素个数
const size = obj => Object.keys(obj).length;
size([1, 2, 3]);
// => 3
size({ a: 1, b: 2 });
// => 2
size('pebbles');
// => 7
思路:使用 Object.keys 方法获取对象属性的数组,再使用 length 属性获取长度即可。
some
遍历数组或对象,判断是否至少有一个元素符合条件
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
思路:使用 some 方法判断是否至少有一个元素符合条件。
sortBy
按照指定的方式对数组进行排序
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]]
思路:使用 sort 方法和传入的函数进行排序即可。
- 感谢你赐予我前进的力量