js数组高阶方法reduce经典用法代码分享
以下是个人在工作中收藏总结的一些关于javascript数组方法reduce
的相关代码片段,后续遇到其他使用这个函数的场景,将会陆续添加,这里作为备忘。
javascript数组那么多方法,为什么我要单挑reduce
方法,一个原因是我对这个方法掌握不够,不能够用到随心所欲。另一个方面,我也感觉到了这个方法的庞大魅力,在许多的场景中发挥着神奇的作用。
理解reduce
函数
reduce() 方法接收一个函数作为累加器(accumulator),数组中的每个值(从左到右)开始缩减,最终为一个值。
arr.reduce([callback, initialValue])
看如下例子:
let arr = [1, 2, 3, 4, 5]; // 10代表初始值,p代表每一次的累加值,在第一次为10
// 如果不存在初始值,那么p第一次值为1
// 此时累加的结果为15
let sum = arr.reduce((p, c) => p + c, 10); // 25
// 转成es5的写法即为:
var sum = arr.reduce(function(p, c) {
console.log(p);
return p + c;
}, 10);
片段一:字母游戏
const anagrams = str => {
if (str.length <= 2) {
return str.length === 2 ? [str, str[1] + str[0]] : str;
}
return str.split("").reduce((acc, letter, i) => {
return acc.concat(anagrams(str.slice(0, i) + str.slice(i + 1)).map(val => letter + val));
}, []);
} anagrams("abc"); // 结果会是什么呢?
reduce
负责筛选出每一次执行的首字母,递归负责对剩下字母的排列组合。
片段二:累加器
const sum = arr => arr.reduce((acc, val) => acc + val, 0);
sum([1, 2, 3]);
片段三:计数器
const countOccurrences = (arr, value) => arr.reduce((a, v) => v === value ? a + 1 : a + 0, 0);
countOccurrences([1, 2, 3, 2, 2, 5, 1], 1);
循环数组,每遇到一个值与给定值相等,即加1,同时将加上之后的结果作为下次的初始值。
片段四:函数柯里化
函数柯里化的目的就是为了储存数据,然后在最后一步执行。
const curry = (fn, arity = fn.length, ...args) =>
arity <= args.length ? fn(...args) : curry.bind(null, fn, arity, ...args);
curry(Math.pow)(2)(10);
curry(Math.min, 3)(10)(50)(2);
通过判断函数的参数取得当前函数的length
(当然也可以自己指定),如果所传的参数比当前参数少,则继续递归下面,同时储存上一次传递的参数。
片段五:数组扁平化
const deepFlatten = arr =>
arr.reduce((a, v) => a.concat(Array.isArray(v) ? deepFlatten(v) : v), []);
deepFlatten([1, [2, [3, 4, [5, 6]]]]);
片段六:生成菲波列契数组
const fibonacci = n => Array(n).fill(0).reduce((acc, val, i) => acc.concat(i > 1 ? acc[i - 1] + acc[i - 2] : i), []);
fibonacci(5);
片段七:管道加工器
const pipe = (...funcs) => arg => funcs.reduce((acc, func) => func(acc), arg);
pipe(btoa, x => x.toUpperCase())("Test");
通过对传递的参数进行函数加工,之后将加工之后的数据作为下一个函数的参数,这样层层传递下去。
片段八:中间件
const dispatch = action => {
console.log('action', action);
return action;
}
const middleware1 = dispatch => {
return action => {
console.log("middleware1");
const result = dispatch(action);
console.log("after middleware1");
return result;
}
}
const middleware2 = dispatch => {
return action => {
console.log("middleware2");
const result = dispatch(action);
console.log("after middleware2");
return result;
}
}
const middleware3 = dispatch => {
return action => {
console.log("middleware3");
const result = dispatch(action);
console.log("after middleware3");
return result;
}
}
const compose = middlewares => middlewares.reduce((a, b) => args => a(b(args))) const middlewares = [middleware1, middleware2, middleware3];
const afterDispatch = compose(middlewares)(dispatch); const testAction = arg => {
return { type: "TEST_ACTION", params: arg };
};
afterDispatch(testAction("1111"));
redux
中经典的compose
函数中运用了这种方式,通过对中间件的重重层叠,在真正发起action的时候触发函数执行。// redux-actions/src/handleAction.js
const handleAction = (type, reducer, defaultState) => {
const types = type.toString();
const [nextReducer, throwReducer] = [reducer, reducer];
return (state = defaultState, action) => {
const { type: actionType } = action;
if (!actionType || types.indexOf(actionType.toString()) === -1) {
return state;
}
return (action.error === true ? throwReducer : nextReducer)(state, action);
}
}
// reduce-reducers/src/index.js
const reduceReducer = (...reducers) => {
return (previous, current) => {
reducers.reduce((p, r) => r(p, current), previous);
}
}
// redux-actions/src/handleActions.js
const handleActions = (handlers, defaultState, { namespace } = {}) => {
// reducers的扁平化
const flattenedReducerMap = flattenReducerMap(handles, namespace);
// 每一种ACTION下对应的reducer处理方式
const reducers = Reflect.ownkeys(flattenedReducerMap).map(type => handleAction(
type,
flattenedReducerMap[type],
defaultState
));
// 状态的加工器,用于对reducer的执行
const reducer = reduceReducers(...reducers);
// reducer触发
return (state = defaultState, action) => reducer(state, action);
}
const reducers = {
totalInEuros: (state, item) => {
return state.euros += item.price * 0.897424392;
},
totalInYen: (state, item) => {
return state.yens += item.price * 113.852;
}
};
const manageReducers = reducers => {
return (state, item) => {
return Object.keys(reducers).reduce((nextState, key) => {
reducers[key](state, item);
return state;
}, {})
}
}
const bigTotalPriceReducer = manageReducers(reducers);
const initialState = { euros: 0, yens: 0 };
const items = [{ price: 10 }, { price: 120 }, { price: 1000 }];
const totals = items.reduce(bigTotalPriceReducer, initialState);
let school = {
name: 'Hope middle school',
created: '2001',
classes: [
{
name: '三年二班',
teachers: [
{ name: '张二蛋', age: 26, sex: '男', actor: '班主任' },
{ name: '王小妞', age: 23, sex: '女', actor: '英语老师' }
]
},
{
name: '明星班',
teachers: [
{ name: '欧阳娜娜', age: 29, sex: '女', actor: '班主任' },
{ name: '李易峰', age: 28, sex: '男', actor: '体育老师' },
{ name: '杨幂', age: 111, sex: '女', actor: '艺术老师' }
]
}
]
};
// 常规做法
school.classes &&
school.classes[0] &&
school.classes[0].teachers &&
school.classes[0].teachers[0] &&
school.classes[0].teachers[0].name
// reduce方法
const get = (p, o) => p.reduce((xs, x) => (xs && xs[x] ? xs[x] : null), o);
get(['classes', 0, 'teachers', 0, 'name'], school); // 张二蛋
const groupBy = (arr, func) =>
arr.map(typeof func === 'function' ? func : val => val[func]).reduce((acc, val, i) => {
acc[val] = (acc[val] || []).concat(arr[i]);
return acc;
}, {});
groupBy([6.1, 4.2, 6.3], Math.floor);
groupBy(['one', 'two', 'three'], 'length');
map
计算出所有的键值,然后再根据建值进行归类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']);
根据给出的键值来遍历,比较对象中是否存在相同键值的的值,然后通过逗号表达式把赋值后的对象赋给下一个的初始值
片段十四:数组中删除指定位置的值
const remove = (arr, func) =>
Array.isArray(arr)
? arr.filter(func).reduce((acc, val) => {
arr.splice(arr.indexOf(val), 1);
return acc.concat(val);
}, []) : [];
const arr = [1, 2, 3, 4];
remove(arr, n => n % 2 == 0);
首先根据filter
函数过滤出数组中符合条件的值,然后使用reduce
在原数组中删除符合条件的值,可以得出最后arr的值变成了[1, 3]
片段十五:promise按照顺序执行
const runPromisesInSeries = ps => ps.reduce((p, next) => p.then(next), Promise.resolve());
const delay = d => new Promise(r => setTimeout(r, d));
const print = args => new Promise(r => r(args));
runPromisesInSeries([() => delay(1000), () => delay(2000), () => print('hello')]);
片段十六:排序
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 = [{ name: 'fred', age: 48 }, { name: 'barney', age: 36 }, { name: 'fly', age: 26 }];
orderBy(users, ['name', 'age'], ['asc', 'desc']);
orderBy(users, ['name', 'age']);
片段十七:选择
const select = (from, selector) =>
selector.split('.').reduce((prev, cur) => prev && prev[cur], from);
const obj = { selector: { to: { val: 'val to select' } } };
select(obj, 'selector.to.val');
js数组高阶方法reduce经典用法代码分享的更多相关文章
- JavaScript系列--JavaScript数组高阶函数reduce()方法详解及奇淫技巧
一.前言 reduce() 方法接收一个函数作为累加器,数组中的每个值(从左到右)开始缩减,最终计算为一个值. reduce() 可以作为一个高阶函数,用于函数的 compose. reduce()方 ...
- JavaScript之数组高阶API—reduce()
一文搞懂JavaScript数组中最难的数组API--reduce() 前面我们讲了数组的一些基本方法,今天给大家讲一下数组的reduce(),它是数组里面非常重要也是比较难的函数,那么这篇文章就好好 ...
- 数组的高阶方法map filter reduce的使用
数组中常用的高阶方法: foreach map filter reduce some every 在这些方法中都是对数组中每一个元素进行遍历操作,只有foreach是没有 ...
- filter,map,reduce三个数组高阶函数的使用
filter ,map ,reduce三个高阶函数的使用 普通方法解决数据问题 const nums1= [10,20,111,222,444,40,50] // 需求1.取出小于100的数字 // ...
- js基础--高阶函数(map,reduce,filter,sort)
高阶函数 一个函数可以接收另一个函数作为参数,这种函数就称之为高阶函数,编写高阶函数,就是让函数的参数能够接收别的函数. function add (x,y,f){return f(x)+f(y)} ...
- JS 数组中对象去重 reduce 用法
对于数组对象,传统的去重方法无能为力,至于forEach().filter()等迭代方法也不好使:真正能做到优雅去重的,是ES5新增加的一个方法——reduce() 高手给的,完美方法 let log ...
- 【js】高阶函数是个什么?
所谓高阶函数,就是函数中可以传入另一个函数作为参数的函数. 简单一张图,方便理解全文. function 高阶函数(函数){} 这是一个高阶函数,f是传入的函数作为参数. 其实高阶函数用的很多.其实平 ...
- js数组中foEach和map的用法详解 jq中的$.each和$.map
数组中foEach和map的用法详解 相同点: 1.都是循环遍历数组(仅仅是数组)中的每一项. 2.forEach() 和 map() 里面每一次执行匿名函数都支持3个参数:数组中的当前项value, ...
- JS的高阶函数
JavaScript的函数其实都指向某个变量.既然变量可以指向函数,函数的参数能接收变量,那么一个函数就可以接收另一个函数作为参数,这种函数就称之为高阶函数. 这就是最简单的高阶函数啦 functio ...
随机推荐
- hihoCoder #1870 : Jin Yong’s Wukong Ranking List-闭包传递(递归) (ACM-ICPC Asia Beijing Regional Contest 2018 Reproduction A) 2018 ICPC 北京区域赛现场赛A
P1 : Jin Yong’s Wukong Ranking List Time Limit:1000ms Case Time Limit:1000ms Memory Limit:512MB Desc ...
- IEDA快捷键
前言 开发工具从eclipse过渡到idea了:在刚开始使用的时候被idea强大的快捷键都惊呆了,这里记录了常见的一些快捷键和小伙伴们分享. 快捷键 鼠标悬停在单词上自动提示 : settings-- ...
- 洛谷——P1223 排队接水
P1223 排队接水 题目描述 有n个人在一个水龙头前排队接水,假如每个人接水的时间为Ti,请编程找出这n个人排队的一种顺序,使得n个人的平均等待时间最小. 输入输出格式 输入格式: 输入文件共两行, ...
- Codeforces Round #424 (Div. 2, rated, based on VK Cup Finals) Cards Sorting(树状数组)
Cards Sorting time limit per test 1 second memory limit per test 256 megabytes input standard input ...
- [BZOJ4537][HNOI2016]最小公倍数(分块+并查集)
4537: [Hnoi2016]最小公倍数 Time Limit: 40 Sec Memory Limit: 512 MBSubmit: 1687 Solved: 607[Submit][Stat ...
- Java并发(一):多线程干货总结
一.进程 线程 进程:一个进程来对应一个程序, 每个进程对应一定的内存地址空间,并且只能使用它自己的内存空间,各个进程间互不干扰. 进程保存了程序每个时刻的运行状态,这样就为进程切换提供了可能.当进程 ...
- 一劳永逸的搞定 flex 布局
一劳永逸的搞定 flex 布局 寻根溯源话布局 一切都始于这样一个问题:怎样通过 CSS 简单而优雅的实现水平.垂直同时居中.记得刚开始学习 CSS 的时候,看到 float 属性不由得感觉眼前一亮, ...
- UIButton中的三个UIEdgeInsets属性
接着昨天的 UIButton中的三个UIEdgeInsets属性 ,今天我们具体谈谈UIButton的contentEdgeInsets.titleEdgeInsets.imageEdgeInsets ...
- Android Parcel对象详解
关于Parcel的使用 在分析Parcel之前,首先按照分析流程,介绍下关于Parcel的相关常规使用. 首先是关于Parcel的获取: Parcel parcle = Parcel.Obtain() ...
- LightOJ - 1265 Island of Survival 期望
题目大意:有一个生存游戏,里面t仅仅老虎,d仅仅鹿,另一个人,每天都要有两个生物碰面,如今有下面规则 1.老虎和老虎碰面.两仅仅老虎就会同归于尽 2.老虎和人碰面或者和鹿碰面,老虎都会吃掉对方 3.人 ...