js 数组方法 reduce 经典用法代码分享

原文地址open in new window

本文给大家整理了很多关于 js 数组方法 reduce 的经典代码片段,能够让大家更好的理解 reduce 的实例用法,一起学习下吧。

reduce() 方法接收一个函数作为累加器 (accumulator),数组中的每个值(从左到右)开始缩减,最终为一个值。

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);
example 字母游戏
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 负责筛选出每一次执行的首字母,递归负责对剩下字母的排列组合。

example 累加器
const sum = (arr) => arr.reduce((acc, val) => acc + val, 0);
sum([1, 2, 3]);
example 计数器
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,同时将加上之后的结果作为下次的初始值。

example 函数柯里化

函数柯里化的目的就是为了储存数据,然后在最后一步执行。

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 (当然也可以自己指定),如果所传的参数比当前参数少,则继续递归下面,同时储存上一次传递的参数。

example 数组扁平化
const deepFlatten = (arr) =>
  arr.reduce((a, v) => a.concat(Array.isArray(v) ? deepFlatten(v) : v), []);
deepFlatten([1, [2, [3, 4, [5, 6]]]]);
example 生成菲波列契数组
const fibonacci = (n) =>
  Array(n)
    .fill(0)
    .reduce(
      (acc, val, i) => acc.concat(i > 1 ? acc[i - 1] + acc[i - 2] : i),
      []
    );
fibonacci(5);
example 管道加工器
const pipe =
  (...funcs) =>
  (arg) =>
    funcs.reduce((acc, func) => func(acc), arg);
pipe(btoa, (x) => x.toUpperCase())("Test");

通过对传递的参数进行函数加工,之后将加工之后的数据作为下一个函数的参数,这样层层传递下去。

example 中间件
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 的时候触发函数执行

example redux-actions 对 state 的加工片段
// 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);
};
example 数据加工器
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);
example 对象空值判断
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); // 张二蛋
example 分组
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 计算出所有的键值,然后再根据建值进行归类

example 对象过滤
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"]);

根据给出的键值来遍历,比较对象中是否存在相同键值的的值,然后通过逗号表达式把赋值后的对象赋给下一个的初始值

example 数组中删除指定位置的值
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]

example 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"),
]);
example 排序
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"]);
example 选择
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");
Last Updated:
Contributors: susheng