【JS基础系列】reduce()的几种常见用法

276 阅读4分钟

reduce()定义

reduce() 方法接收一个函数作为累加器,数组中的每一个元素依次执行回调函数,不包括数组中被删除或从未被赋值的元素,数组中的每个值(从左到右)开始缩减,最终计算为一个值。

⚠️ reduce() 对于空数组是不会执行回调函数的。

const arr1 = [];
arr1.reduce((prev, cur, index) => {
    console.log(cur);
});

const arr2 = [1, 2, 3];
arr2.reduce((prev, cur, index) => {
    console.log(cur);
});

reduce()语法

array.reduce(function(previousValue, currentValue, currentIndex, arr), initialValue)

参数描述

  • function(previousValue,currentValue, index,arr):必需。用于执行每个数组元素的函数。

    • previousValue:必需。初始值initialValue, 或者上一次调用回调后的返回值。
    • currentValue:必需。当前元素。
    • currentIndex:可选。当前元素的索引。
    • arr:可选。当前元素所属的数组对象。
  • initialValue:可选。传递给函数的初始值。

如果没有提供 initialValue,reduce 会从索引1的地方开始执行 callback 方法,跳过第一个索引。如果提供 initialValue,从索引0开始。

有initialValue和无initialValue对比

// 无initialValue,索引值会从1的地方开始执行回调,跳过第一个索引
const arr1 = [1, 2, 3, 4];
arr1.reduce((prev, cur, index) => {
    console.log(cur);
});

// 输出结果
// 2
// 3
// 4

// 有initialValue,从索引0开始
arr1.reduce((prev, cur, index) => {
    console.log(cur);
}, 100);

// 输出结果
// 1
// 2
// 3
// 4

reduce()用法

数组求和

const sum = arr => {
    if (!Array.isArray(arr) || !arr.length) {
        throw new Error("参数必须是非空数组");
    }
    return arr.reduce((prev, cur) => {
        return prev + cur;
    })
};

// 测试例子
const arr = [1, 2, 3, 4, 5];
sum(arr); 
// 15

数组乘积

const product = arr => {
    if (!Array.isArray(arr) || !arr.length) {
        throw new Error("参数必须是非空数组");
    }
    return arr.reduce((prev, cur) => {
        return prev * cur;
    })
};

// 测试例子
const arr = [1, 2, 3, 4, 5];
product(arr); 
// 120

数组去重

const unique = arr => {
    if (!Array.isArray(arr) || !arr.length) {
        throw new Error("参数必须是非空数组");
    }
    return arr.reduce((prev, cur) => {
        if(!prev.includes(cur)) {
            prev.push(cur);
        }
        return prev;
    }, []);
};

// 测试例子
const arr = [1, 2, 1, 3, 5, 5, 1];
unique(arr); 
// [1, 2, 3, 5]

统计数组中相同元素个数

const getArrItemNUm = arr => {
    if (!Array.isArray(arr) || !arr.length) {
        throw new Error("参数必须是非空数组");
    }
    return arr.reduce((prev, cur) => {
        if (!prev[cur]) {
            prev[cur] = 1;
        } else {
            prev[cur]++
        }
        return prev;
    }, {});
};

// 测试例子
const arr = [1, 2, 1, 3, 5, 5, 1];
getArrItemNUm(arr);
// {1: 3, 2: 1, 3: 1, 5: 2}

字符串字母个数统计

const getStrItemNUm = str => {
    if (!str || !str.length) {
        throw new Error("参数必须是非空字符串");
    }
    const arr = str.split('');
    return arr.reduce((prev, cur) => {
        if (!prev[cur]) {
            prev[cur] = 1;
        } else {
            prev[cur]++
        }
        return prev;
    }, {});
};

// 测试例子
const arr = "abdfgscacsdedfdascdsdrdscdewsa";
getStrItemNUm(arr);
// {a: 4, b: 1, c: 4, d: 8, e: 2, f: 2, g: 1, r: 1, s: 6, w: 1}

根据某个key值实现分组

示例:有一个数组是[{name: "ss1", age: 18, code: 123}, {name: "ss2", age: 19, code: 234}, {name: "ss2", age: 20, code: 123}, {name: "ss1", age: 18}],根据某个key值进行分组,比如根据code进行分组,结果是

function groupBy(filedname, arr) {
    if (!Array.isArray(arr) || !arr.length) {
        throw new Error("参数必须是非空数组");
    }
    if (!filedname) {
        return arr;
    }
    return arr.reduce((prev, cur) => {
        if(cur.hasOwnProperty(filedname)) {
            prev[cur[filedname]] = prev[cur[filedname]] || [];
            prev[cur[filedname]].push(cur);
        }
        return prev;
    }, {});
}
// 测试例子
const arr = [
    {name: "ss1", age: 18, code: 123},
    {name: "ss2", age: 19, code: 234},
    {name: "ss2", age: 20, code: 123},
    {name: "ss1", age: 18}
];
groupBy("code", arr);
// 输出结果
/** 
{
    123: [{name: "ss1", age: 18, code: 123}, {name: "ss2", age: 20, code: 123}], 
    234: [{name: "ss2", age: 19, code: 234}]
}
*/
groupBy("name", arr);
// 输出结果
/** 
{
    ss1: [{name: "ss1", age: 18, code: 123}, {name: "ss1", age: 18}], 
    ss2: [{name: "ss2", age: 19, code: 234}, {name: "ss2", age: 20, code: 123}]
}
*/

实现flat

function _flat(arr, depth) {
    if (!Array.isArray(arr) || !arr.length || depth <= 0) {
        return arr;
    }
    return arr.reduce((prev, cur) => {
        if(Array.isArray(cur)) {
            return prev.concat(_flat(cur, depth - 1));
        } else {
            return prev.concat(cur);
        }
    }, []);
}

// 测试例子
const arr = [1, 2, 3, 4, [1, 2, 3, [1, 2, 3, [1, 2, 3]]]];
_flat(arr, 1); // [1, 2, 3, 4, 1, 2, 3, [1, 2, 3, [1, 2, 3]]]
_flat(arr, 2); // [1, 2, 3, 4, 1, 2, 3, 1, 2, 3, [1, 2, 3]]
_flat(arr, Infinity); // [1, 2, 3, 4, 1, 2, 3, 1, 2, 3, 1, 2, 3]

normalize转换为树形结构

  1. 例如将 [{id: 1}, {id: 2, pId: 1}, ...] 的重复数组(有重复数据)转成树形结构的数组[{id: 1, child: [{id: 2, pId: 1}]}, ...] (需要去重)
function transformToTree(arr) {
    if (!Array.isArray(arr) || !arr.length) {
        throw new Error("参数必须是非空数组");
    }
    const res = [];
    const map = arr.reduce((prev, cur) => {
        prev[cur.id] = cur;
        return prev;
    }, {});
    for(let k of Object.values(map)) {
        if(!k.pId) {
            res.push(k);
        }
        if(k.pId in map) {
            const parent = map[k.pId];
            parent.child = parent.child || [];
            parent.child.push(k)
        }
    }
    return res;
}
// 测试例子
const arr = [{id: 1}, {id:2, pId: 1}, {id: 3, pId: 2}, {id: 4}, {id:3, pId: 2}, {id: 5, pId: 4}];
transformToTree(arr);
// [{id: 1, child: [{id: 2, pId: 1, child: [{id: 3, pId: 2}]}]},{id: 4, child: [{id: 5, pId: 4}]}]
  1. 把 entry 转换成如下对象 var entry = { 'a.b.c.dd': 'abcdd', 'a.d.xx': 'adxx', 'a.e': 'ae' }; 要求转换成如下对象 var output = { a: { b: { c: { dd: 'abcdd' } }, d: { xx: 'adxx' }, e: 'ae' } }
function transformToJson(obj) {
    const res = {};
    const keys = Object.keys(obj);
    for(let k of keys) {
        const keyArr = k.split('.');
        keyArr.reduce((prev, cur, index, arr) => {
            if(index === arr.length - 1) {
                prev[cur] = obj[k];
                return;
            }
            prev[cur] = prev[cur] || {};
            return prev[cur];
        }, res);
    }
    return res;
}
// 测试例子
const entry = {
    'a.b.c.dd': 'abcdd',
    'a.d.xx': 'adxx',
    'a.e': 'ae'
};
transformToJson(entry);
// { a: { b: { c: { dd: "abcdd" } }, d: { xx: "adxx" }, e: "ae" } }
  1. 上一题的反向操作 把 entry 转换成如下对象 var entry = { a: { b: { c: { dd: 'abcdd' } }, d: { xx: 'adxx' }, e: 'ae' } } 要求转换成如下对象 var output = { 'a.b.c.dd': 'abcdd', 'a.d.xx': 'adxx', 'a.e': 'ae' };
const transformFun = function (obj, tmpKey = '', res = {}, nums = 0) {
    for (let k in obj) {
        const item = obj[k];
        if (typeof item === 'object' && item !== null && Object.keys(item).length > 0) {
            if (nums !== 0) {
                transformFun(item, tmpKey + '.' + k, res, nums + 1);
            } else {
                transformFun(item, k, res, nums + 1);
            }
        } else {
            if (nums !== 0) {
                res[tmpKey + '.' + k] = item;
            } else {
                res[k] = item;
            }
        }
    }
    return res;
};

var entry = {
    a: {
        b: {
            c: {
                dd: 'abcdd'
            }
        },
        d: {
            xx: 'adxx'
        },
        e: 'ae'
    }
}

transformFun(entry) 
/* {
    "a.b.c.dd": "abcdd",
    "a.d.xx": "adxx",
    "a.e": "ae",
    "f": "xxxxx",
    "g": {}
} */