js的reduce实现以及使用场景

MDN镇文
https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce

手写实现reduce

实现方式一

Array.prototype.reduce = function (fn, init) {
    if (typeof fn !== 'function') {
        throw new TypeError('xxx');
    }

    initArr = this;
    arr = initArr.concat();
    if (init) arr.unshift(init);
    let index, newVal;
    while (arr.length > 1) {
        index = initArr.length - arr.length + 1;
        newVal = fn.call(null, arr[0], arr[1], index, initArr);

        arr.splice(0, 2, newVal)
    }

    return newVal
}

实现方式二 (递归)

const reduceHelper = (fn, acc, idx, array) => {
    if (array.length === 0) return acc
    const [head, ...tail] = array
    idx++
    return reduceHelper(fn, fn(acc, head, idx, array), idx, tail)
}

Array.prototype.myReduce = function (cb, initialValue) {
    const array = this
    const [head, ...tail] = array
    const startIndex = initialValue ? -1 : 0

    return initialValue ? reduceHelper(cb, initialValue, startIndex, array) : reduceHelper(cb, head, startIndex, tail)
}

应用场景

一: 累加

const sum = arr => arr.reduce((acc, val) => acc + val, 0);
sum([1, 2, 3]);

二: 计数器

let names = ['Alice', 'Bob', 'Tiff', 'Bruce', 'Alice'];
let nameNum = names.reduce((pre, cur) => {
    if (cur in pre) {
        pre[cur]++
    } else {
        pre[cur] = 1
    }
    return pre
}, {})
console.log(nameNum); // //{Alice: 2, Bob: 1, Tiff: 1, Bruce: 1}

三: 数组扁平化

const deepFlatten = arr =>
    arr.reduce((a, v) => a.concat(Array.isArray(v) ? deepFlatten(v) : v), []);
console.log(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), []);
console.log(fibonacci(5));

五: 中间件

redux中经典的compose函数中运用了这种方式, 通过对中间件的重重层叠, 在真正发起action的时候触发函数执行.

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) => {
    return function (args) {
        return a(b(args))
    }
})

const middlewares = [middleware1, middleware2, middleware3];
const afterDispatch = compose(middlewares)(dispatch);

// const afterDispatch = middleware1(middleware2(middleware3(dispatch)));

const testAction = arg => {
    return { type: "TEST_ACTION", params: arg };
};
afterDispatch(testAction("1111"));

六: 对象空值判断

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 pick = (obj, arr) =>
    arr.reduce(function (acc, curr) {
        if (curr in obj) {
            acc[curr] = obj[curr]
            return acc;
        }
    }, {});

// 也可以简写成
const pick2 = (obj, arr) =>
    arr.reduce((acc, curr) => (curr in obj && (acc[curr] = obj[curr]), acc), {});
pick({ a: 1, b: '2', c: 3 }, ['a', 'c']);

八: 分组

首先通过map计算出所有的键值,然后再根据建值进行归类

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');

九: 数组删除指定位置的值

首先根据filter函数过滤出数组中符合条件的值,然后使用reduce在原数组中删除符合条件的值,可以得出最后arr的值变成了[1, 3]

const remove = (arr, fn) =>{
    return arr.filter(fn).reduce((acc, cur) => {
        arr.splice(arr.indexOf(cur), 1);
        return acc.concat(cur);
    }, []);
}
const arr = [1, 7, 3, 4, 8];
console.log(remove(arr, n => n % 3 == 1))
console.log(arr)

十: 字母游戏

reduce负责筛选出每一次执行的首字母,递归负责对剩下字母的排列组合。

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");

十一: 排序

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

推荐阅读更多精彩内容

  • 第5章 引用类型(返回首页) 本章内容 使用对象 创建并操作数组 理解基本的JavaScript类型 使用基本类型...
    大学一百阅读 3,211评论 0 4
  • 一、数组定义 array() 1、索引数组 在一个变量中,存储一个或多个值。数组中的每一个元素都有一个访问ID,根...
    竹与豆阅读 526评论 0 0
  • 第一章 错误处理: 错误: 程序运行过程中,导致程序无法正常执行的现象(即bug) 现象: 程序一旦出错,默认会报...
    fastwe阅读 1,093评论 0 1
  • 1.strlen()与mb_strlen()的作用分别是什么? strlen()和mb_strlen()都是作用于...
    落魄PHP女程序员阅读 345评论 0 2
  • 函数和对象 1、函数 1.1 函数概述 函数对于任何一门语言来说都是核心的概念。通过函数可以封装任意多条语句,而且...
    道无虚阅读 4,521评论 0 5