30-seconds-of-code

Array(数组相关函数)

  • chunk

将数组分割成指定长度的新数组。

使用Array.from()方法从一个类似数组或可迭代对象中创建一个新的数组实例。Array.from()有一个可选参数mapFn,让你可以在最后生成的数组上在执行一次map方法,也就是说Array.from(obj,mapFn,thisArg) 等同于Arrat.from(obj).map(mapFn,thisArg);

使用Array.slice()方法将生成的新数组按照指定的长度截成一小块数组。

如果原数组最后一部分不能被分割成指定长度,那么最后一块数组将保留剩下的部分。

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

chunk([1, 2, 3, 4, 5], 2); // [[1,2],[3,4],[5]]
  • compact

保留数组中的非空值。

使用Array.filter() 方法移除数组的‘空值’(undefined,null,'',,false and NaN)

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

compact([0, 1, false, 2, '', 3, 'a', 'e' * 23, NaN, 's', 34]); // [ 1, 2, 3, 'a', 's', 34 ]
  • countBy

根据给定的函数对数组的元素进行分组,并返回每个组中元素的数量。

使用Array.map()将原数组的值映射到函数或者属性名。

使用Array.reduce来创建对象,其中的key来自于映射的结果。

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

countBy([6.1, 4.2, 6.3], Math.floor); // {4: 1, 6: 2}
countBy(['one', 'two', 'three'], 'length'); // {3: 2, 5: 1}
  • countOccurrences

计算某个值在数组中出现的次数。

使用Array.reduce()方法遍历数组,每当出现指定值时,计数加1.

const countOccurrences = (arr, val) => arr.reduce((a, v) => (v === val ? a + 1 : a + 0), 0);

countOccurrences([1, 1, 2, 1, 2, 3], 1); // 3
  • deepFlatten

扁平化数组。

使用递归。将Array.concat()与空数组([])和展开运算符(...)一起使用以扁平化数组。递归地扁平数组的每个元素。

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

deepFlatten([1, [2], [[3], 4], 5]); // [1,2,3,4,5]
  • difference

返回两个数组中的不同值。

用数组b创建一个Set,然后在数组a使用Array.filter()方法,只返回b中不包含的值。

const difference = (a, b) => {
const s = new Set(b);
return a.filter(x => !s.has(x));
};

difference([1, 2, 3], [1, 2, 4]); // [3]
  • distinctValuesOfArray

去除数组中重复的值。

使用Set和...扩展运算符去掉数组中所有的重复值。

const distinctValuesOfArray = arr => [...new Set(arr)];

distinctValuesOfArray([1, 2, 2, 3, 4, 4, 5]); // [1,2,3,4,5]
  • dropElements

删除数组中的元素直到传入的函数返回值为true,返回数组中剩余的值。

循环访问数组,使用Array.slice()删除数组的第一个元素,直到函数的返回值为true。返回其余的元素。

const dropElements = (arr, func) => {
while (arr.length > 0 && !func(arr[0])) arr = arr.slice(1);
return arr;
};

dropElements([1, 2, 3, 4], n => n >= 3); // [3,4]
  • dropRight

返回从右边开始删除指定个元素的新数组。

使用Array.slice()从右边开始删除指定个元素,并返回剩余元素组成的新数组。

const dropRight = (arr, n = 1) => arr.slice(0, -n);

dropRight([1, 2, 3]); // [1,2]
dropRight([1, 2, 3], 2); // [1]
dropRight([1, 2, 3], 42); // []
  • everyNth

返回数组中的每第n个元素

const everyNth = (arr, nth) => arr.filter((e, i) => i % nth === nth - 1);
  • filterNonUnique

返回数组中独一无二的值。

const filterNonUnique = arr => arr.filter(i => arr.indexOf(i) === arr.lastIndexOf(i));

filterNonUnique([1, 2, 2, 3, 4, 4, 5]); // [1,3,5]
  • findLast

返回最后一个使传入函数返回为true的值。

使用Array.filter()来过滤得到使传入函数返回true的所有值,最后使用slice(-1)得到最后一个值。

const findLast = (arr, fn) => arr.filter(fn).slice(-1);

findLast([1, 2, 3, 4], n => n % 2 === 1); // 3
  • flatten

扁平化数组,参数depth=1时只扁平一层,即二维变成一维,N维变成N-1维。

const flatten = (arr, depth = 1) =>
depth != 1
? arr.reduce((a, v) => a.concat(Array.isArray(v) ? flatten(v, depth - 1) : v), [])
: arr.reduce((a, v) => a.concat(v), []);
  • forEachRight

从数组右边开始对每个元素执行给定的函数。

const forEachRight = (arr, callback) =>
arr
.slice(0)
.reverse()
.forEach(callback);

forEachRight([1, 2, 3, 4], val => console.log(val)); // '4', '3', '2', '1'
  • groupBy

基于给定的函数对数组中的元素进行分类。

使用Array.map()将数组的值映射到函数或属性名称。使用Array.reduce()来创建一个对象,其中的key是从映射结果中产生的。

和countBy类似,只不过countBy中每组的值为个数,而groupBy每组的值是由具体元素组成的数组。

const groupBy = (arr, fn) =>
arr.map(typeof fn === 'function' ? fn : val => val[fn]).reduce((acc, val, i) => {
acc[val] = (acc[val] || []).concat(arr[i]);
return acc;
}, {});

groupBy([6.1, 4.2, 6.3], Math.floor); // {4: [4.2], 6: [6.1, 6.3]}
groupBy(['one', 'two', 'three'], 'length'); // {3: ['one', 'two'], 5: ['three']}
  • indexOfAll

返回指定值在数组中的所有索引。如果该值在数组中不存在,那么返回[].

使用Array.forEach()循环元素和Array.push()来存储索引匹配的元素。返回索引数组。

const indexOfAll = (arr, val) => {
const indices = [];
arr.forEach((el, i) => el === val && indices.push(i));
return indices;
};

indexOfAll([1, 2, 3, 1, 2, 3], 1); // [0,3]
indexOfAll([1, 2, 3], 4); // []
  • intersection

返回由两个数组中均有的值组成的数组。

const intersection = (a, b) => {
const s = new Set(b);
return a.filter(x => s.has(x));
};

intersection([1, 2, 3], [4, 3, 2]); // [2,3]
  • isSorted

如果一个数组的元素以升序排序,返回1;降序返回-1;无序则返回0.

计算前两个元素的排序方向。使用Object.entries()循环访问数组对象并将它们成对比较。如果方向改变,则返回0;如果到达最后一个元素,则返回方向。

const isSorted = arr => {
const direction = arr[0] > arr[1] ? -1 : 1;
for (let [i, val] of arr.entries())
if (i === arr.length - 1) return direction;
else if ((val - arr[i + 1]) * direction > 0) return 0;
};

isSorted([0, 1, 2, 2]); // 1
isSorted([4, 3, 2]); // -1
isSorted([4, 3, 5]); // 0
  • mapObject

使用函数将数组的值映射到对象,其中键值对由原始值作为键和映射值组成。

使用匿名内部函数作用域来声明未定义的内存空间,使用闭包存储返回值。使用一个新的数组来存储带有数据集函数映射的数组和一个逗号运算符来返回第二步,而不需要从一个上下文移动到另一个上下文(由于关闭和操作顺序)。

const mapObject = (arr, fn) =>
(a => (
(a = [arr, arr.map(fn)]), a[0].reduce((acc, val, ind) => ((acc[val] = a[1][ind]), acc), {})
))();


const squareIt = arr => mapObject(arr, a => a * a);
squareIt([1, 2, 3]); // { 1: 1, 2: 4, 3: 9 }
  • partition

根据所提供的函数对每个元素的返回值是否为true将这些元素分成两个数组。

使用Array.reduce()来创建两个数组的数组。使用Array.push()添加fn返回true的元素到第一个数组,而fn返回false的元素到第二个元素。

const partition = (arr, fn) =>
arr.reduce(
(acc, val, i, arr) => {
acc[fn(val, i, arr) ? 0 : 1].push(val);
return acc;
},
[[], []]
);
const users = [{ user: 'barney', age: 36, active: false }, { user: 'fred', age: 40, active: true }];
partition(users, o => o.active);
// [[{ 'user': 'fred', 'age': 40, 'active': true }],[{ 'user': 'barney', 'age': 36, 'active': false }]]
  • pick

从对象中挑选与给定键对应的键值对。

如果密钥存在于obj中,则使用Array.reduce()将过滤/拾取的键转换回具有相应键 - 值对的对象。

const pick = (obj, arr) =>
arr.reduce((acc, curr) => (curr in obj && (acc[curr] = obj[curr]), acc), {});

pick({ a: 1, b: '2', c: 3 }, ['a', 'c']); // { 'a': 1, 'c': 3 }
  • pull

改变原数组过滤出指定的元素。

使用Array.filter()和Array.includes()来提取不需要的值。使用Array.length = 0,通过将数组的长度重置为零来改变传入的数组,Array.push(\)存取需要的值。

const pull = (arr, ...args) => {
let argState = Array.isArray(args[0]) ? args[0] : args;
let pulled = arr.filter((v, i) => !argState.includes(v));
arr.length = 0;
pulled.forEach(v => arr.push(v));
};

let myArray = ['a', 'b', 'c', 'a', 'b', 'c'];
pull(myArray, 'a', 'c'); // myArray = [ 'b', 'b' ]
  • pullAtIndex

改变原始数组以滤除指定索引处的值。

用Array.filter()和Array.includes()来提取不需要的值。使用Array.length = 0,通过将数组的长度重置为零来改变传入的数组,Array.push()用来存储需要的值。

const pullAtIndex = (arr, pullArr) => {
let removed = [];
let pulled = arr
.map((v, i) => (pullArr.includes(i) ? removed.push(v) : v))
.filter((v, i) => !pullArr.includes(i));
arr.length = 0;
pulled.forEach(v => arr.push(v));
return removed;
};

let myArray = ['a', 'b', 'c', 'd'];
let pulled = pullAtIndex(myArray, [1, 3]); // myArray = [ 'a', 'c' ] , pulled = [ 'b', 'd' ]
  • reducedFilter

根据条件过滤一个对象数组,同时过滤未指定的键key。

使用Array.filter()根据函数fn过滤数组,以便得到 条件返回真值的对象。在已过滤的数组上,使用Array.map()使用Array.reduce()返回新对象,以过滤不存在keys数组中的值。

const reducedFilter = (data, keys, fn) =>
data.filter(fn).map(el =>
keys.reduce((acc, key) => {
acc[key] = el[key];
return acc;
}, {})
);

const data = [
{
id: 1,
name: 'john',
age: 24
},
{
id: 2,
name: 'mike',
age: 50
}
];

reducedFilter(data, ['id', 'name'], item => item.age > 24); // [{ id: 2, name: 'mike'}]
  • remove

从给定函数返回false的数组中移除元素。

使用Array.filter()来查找返回真值的数组元素,使用Array.reduce()来移除使用Array.splice()的元素。函数被调用三个参数(值,索引,数组)。

const remove = (arr, func) =>
Array.isArray(arr)
? arr.filter(func).reduce((acc, val) => {
arr.splice(arr.indexOf(val), 1);
return acc.concat(val);
}, [])
: [];

remove([1, 2, 3, 4], n => n % 2 == 0); // [2, 4]
  • sample

从数组中返回一个随机元素。

使用Math.random()生成一个随机数,并用Math.floor()将其乘以长度并将其四舍五入到最接近的整数。这个方法也适用于字符串。

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

随机化一个数组的值的顺序,返回一个新的数组。 使用Fisher-Yates算法重新排列数组的元素。

const shuffle = ([...arr]) => {
let m = arr.length;
while (m) {
const i = Math.floor(Math.random() * m--);
[arr[m], arr[i]] = [arr[i], arr[m]];
}
return arr;
};

const foo = [1, 2, 3];
shuffle(foo); // [2,3,1], foo = [1,2,3]
  • similarity

返回出现在两个数组中的元素数组。 使用Array.filter()删除不是值的一部分的值,使用Array.includes()确定。

const similarity = (arr, values) => arr.filter(v => values.includes(v));

similarity([1, 2, 3], [1, 2, 4]); // [1,2]
  • sortedIndex

返回值为插入到数组中的最低索引,以保持其排序顺序。

检查数组是否按降序排序。使用Array.findIndex()查找元素应插入的适当索引

const sortedIndex = (arr, n) => {
const isDescending = arr[0] > arr[arr.length - 1];
const index = arr.findIndex(el => (isDescending ? n >= el : n <= el));
return index === -1 ? arr.length : index;
};

sortedIndex([5, 3, 2, 1], 4); // 1
sortedIndex([30, 50], 40); // 1
  • symmetricDifference

返回两个数组之间的对称差异。 从每个数组中创建一个Set,然后在每个数组上使用Array.filter(),只保留其他值不包含的值。

const symmetricDifference = (a, b) => {
const sA = new Set(a),
sB = new Set(b);
return [...a.filter(x => !sB.has(x)), ...b.filter(x => !sA.has(x))];
};

symmetricDifference([1, 2, 3], [1, 2, 4]); // [3,4]
  • tail

返回数组中除第一个元素外的所有元素。 返回Array.slice(1)如果数组的长度大于1,则返回整个数组。

const tail = arr => (arr.length > 1 ? arr.slice(1) : arr);

tail([1, 2, 3]); // [2,3]
tail([1]); // [1]
  • take

返回从头开始删除n个元素的数组。 使用Array.slice()创建一个包含从头开始的n个元素的数组。

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

take([1, 2, 3], 5); // [1, 2, 3]
take([1, 2, 3], 0); // []
  • takeRight

返回从最后删除n个元素的数组。 使用Array.slice()来创建数组的一个片段,其中n个元素从末尾取出。

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

takeRight([1, 2, 3], 2); // [ 2, 3 ]
takeRight([1, 2, 3]); // [3]
  • union

返回两个数组中的任何一个元素。 用a和b的所有值创建一个Set并转换成一个数组。

const union = (a, b) => Array.from(new Set([...a, ...b]));

union([1, 2, 3], [4, 3, 2]); // [1,2,3,4]
  • without

筛选出具有指定值之一的数组元素。 使用Array.filter()来创建一个数组,排除(使用!Array.includes())所有给定的值。

const without = (arr, ...args) => arr.filter(v => !args.includes(v));

without([2, 1, 2, 3], 1, 2); // [3]
  • zip

创建一个元素数组,根据原始数组中的位置进行分组。

使用Math.max.apply()获取参数中最长的数组。用这个长度作为返回值创建一个数组,并使用Array.from()和map-function来创建一个分组元素数组。如果参数数组的长度不同,则在未找到值的情况下使用undefined.

const zip = (...arrays) => {
const maxLength = Math.max(...arrays.map(x => x.length));
return Array.from({ length: maxLength }).map((_, i) => {
return Array.from({ length: arrays.length }, (_, k) => arrays[k][i]);
});
};

zip(['a', 'b'], [1, 2], [true, false]); // [['a', 1, true], ['b', 2, false]]
zip(['a'], [1, 2], [true, false]); // [['a', 1, true], [undefined, 2, false]]
  • zipObject

给定一个有效键名的数组和一个键值的数组,返回一个把属性名和属性值关联起来的对象。 因为对象的键值可以是

undefined,键名不能是,所以用提供键名的数组使用Array.prototype.reduce()来决定结果对象的结构。

const zipObject = (props, values) =>
props.reduce((obj, prop, index) => ((obj[prop] = values[index]), obj), {});

zipObject(['a', 'b', 'c'], [1, 2]); // {a: 1, b: 2, c: undefined}
zipObject(['a', 'b'], [1, 2, 3]); // {a: 1, b: 2}

Funtion相关函数

  • chainAsync

链式异步函数。

循环遍历包含异步事件的函数数组,在每个异步事件完成时再调用。

const chainAsync = fns => {
let curr = 0;
const next = () => fns[curr++](next);
next();
};

chainAsync([
next => {
console.log('0 seconds');
setTimeout(next, 1000);
},
next => {
console.log('1 second');
}
]);
  • compose

执行从右到左的函数组合。

使用Array.reduce()来执行从右到左的函数组合。最后(最右边的)函数可以接受一个或多个参数;其余的函数必须是一元的。

const compose = (...fns) => fns.reduce((f, g) => (...args) => f(g(...args)));

const add5 = x => x + 5;
const multiply = (x, y) => x * y;
const multiplyAndAdd5 = compose(add5, multiply);
multiplyAndAdd5(5, 2); // 15
  • curry

函数柯里化。

使用递归。如果提供的参数(args)数量足够,则调用传递的函数fn。否则,返回一个curried函数fn,处理剩下的参数。

如果你想要一个接受可变数量参数的函数,你可以选择将参数个数传递给第二个参数arity。

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

curry(Math.pow)(2)(10); // 1024
curry(Math.min, 3)(10)(50)(2); // 2
  • defer

缓存调用一个函数,直到当前的调用堆栈已经清除。

使用setTimeout(),超时时间为1毫秒,将新事件添加到浏览器事件队列,并允许渲染引擎完成其工作。使用spread(...)运算符为函数提供任意数量的参数。

const defer = (fn, ...args) => setTimeout(fn, 1, ...args);

// Example A:
defer(console.log, 'a'), console.log('b'); // logs 'b' then 'a'

// Example B:
document.querySelector('#someElement').innerHTML = 'Hello';
longRunningFunction(); //Browser will not update the HTML until this has finished
defer(longRunningFunction); // Browser will update the HTML then run the function
  • functionName

打印函数名。

使用console.debug()和传入方法的name属性将方法名称记录到控制台的调试通道。

const functionName = fn => (console.debug(fn.name), fn);

// example
functionName(Math.max); // max (logged in debug channel of console)
  • memoize

返回memoize(缓存函数)

通过实例化一个新的Map对象来创建空的缓存。首先检查指定输入值的函数输出是否已经被缓存,如果没有,则返回一个函数,该函数将单个参数提供给memoized函数。必须使用函数关键字才能允许memoized函数在必要时更改其上下文。允许通过将其设置为返回函数的属性来访问缓存。

const memoize = fn => {
const cache = new Map();
const cached = function(val) {
return cache.has(val) ? cache.get(val) : cache.set(val, fn.call(this, val)) && cache.get(val);
};
cached.cache = cache;
return cached;
};

const anagramsCached = memoize(anagrams);
anagramsCached('javascript'); // takes a long time
anagramsCached('javascript'); // returns virtually instantly since it's now cached
console.log(anagramsCached.cache); // The cached anagrams map
  • once

只允许函数调用一次。

使用一个闭包,和一个标志位,调用时将标志位设置为true,一旦函数被调用一次后,阻止它被再次调用。

const once = fn =&gt; {
let called = false;
return function\(...args\) {
if \(called\) return;
called = true;
return fn.apply\(this, args\);
};
};

const startApp = function\(event\) {
console.log\(this, event\); // document.body, MouseEvent
};
document.body.addEventListener\('click', once\(startApp\)\); // only runs `startApp` once upon click
  • sleep

延迟异步函数的执行。使其休眠,并返回一个promise。

const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));

async function sleepyWork() {
console.log("I'm going to sleep for 1 second.");
await sleep(1000);
console.log('I woke up after 1 second.');
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,053评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,527评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,779评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,685评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,699评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,609评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,989评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,654评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,890评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,634评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,716评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,394评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,976评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,950评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,191评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,849评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,458评论 2 342