JavaScript 实现 reduce() 方法函数

2023-11-02

更新:增加递归实现的方法
更新:重构非递归实现的方法


思路

与之前两篇文章( map()的实现filter()的实现 )中的迭代方法不一样,reduce() 是归并方法。

reduce 接收两个参数:

  • 第一个参数是在每一项上调用的函数
    • 该函数接收 4 个参数:
      • 前一个值 prev
        当前值 cur
        项的索引 index
        数组对象 array
  • 第二个可选参数是作为归并基础的初始值

reduce 方法返回一个最终的值

代码表示:

arr.reduce(function(prev, cur, index, arr){}, initialValue)

归并

与之前的迭代不同,归并不是对每一项都执行目标函数,而是可以概括为如下两步:

  • 不断地对数组的前两项“取出”,对其执行目标函数,计算得到的返回值
  • 把上述返回值“填回”数组首部,作为新的 array[0]
  • 持续循环执行这个过程,直到数组中每一项都访问了一次
  • 返回最终结果

举例说明

对数组 [1,2,3] 归并执行 (prev, cur) => prev + cur,流程如图:

[1, 2, 3] // 取出 1 + 2 ,填回 3
[3, 3] // 取出 3 + 3 ,填回 6
[6] // 最终结果为 6

所以得到 6 。

实现

第一版

根据这个思路,得到第一版代码如下

// 第一版
Array.prototype.fakeReduce = function fakeReduce(fn, base) {
  // let arr = base ? this.unshift(base) : this;// 首进,返回新数组的长度,影响原数组 故不能这么写
  let initialArr = this;
  let arr = initialArr.concat(); //得到副本

  if (base) arr.unshift(base); // 当存在归并基础值的参数时,将其从数组首部推入
  let index;

  while (arr.length > 2) {
    index = initialArr.length - arr.length + 1;
    let newValue = fn.call(null, arr[0], arr[1], index, initialArr);
    arr.splice(0, 2); // 删除前两项,影响原数组
    arr.unshift(newValue);// 把 fn(arr[0],arr[1]) 的结果从数组首部推入
  }
  index += 1;
  let result = fn.call(null, arr[0], arr[1], index, initialArr);
  return result;
};

注意点:

  • 队列方法 unshift()

    • 可以从数组首部加入任意个项,
    • 返回值是新数组的长度
    • 影响原数组
  • splice() 方法,高程三将其誉为最强大的数组方法

    • 删除任意数量的项
      • 指定 2 个参数: (删除起始位置, 删除项个数)
    • 插入任意数量的项
      • 指定 3 个参数: (起始位置,0,要插入的项)
      • 第二个参数 0 即为要删除的个数
    • 替换,即删除任意数量的项的同时,插入任意数量的项
      • 指定 3 个参数:(起始位置,要删除的个数, 要插入的任意数量的项)
    • 返回值
      • 始终是一个数组,包含从原始数组中删除的项。
      • 若未删除任何项,返回空数组
    • 影响原数组

改进版

从上面的总结可以看出,splice() 方法完全可以取代 unshift() 方法。

而且,第一版中存在一些重复代码,也可以改进。

由此得到第二版代码

// 第二版
Array.prototype.fakeReduce = function fakeReduce(fn, base) {

  let initialArr = this;
  let arr = initialArr.concat();

  if (base) arr.unshift(base);
  let index, newValue;

  while (arr.length > 1) {
    index = initialArr.length - arr.length + 1;
    newValue = fn.call(null, arr[0], arr[1], index, initialArr);

    arr.splice(0, 2, newValue); // 直接用 splice 实现替换
  }

  return newValue;
};

检测:

let arr = [1, 2, 3, 4, 5];
let sum = arr.fakeReduce((prev, cur, index, arr) => {
  console.log(prev, cur, index, arr);
  return prev * cur;
}, 100);

console.log(sum);

输出:

100 1 0 [ 1, 2, 3, 4, 5 ]
100 2 1 [ 1, 2, 3, 4, 5 ]
200 3 2 [ 1, 2, 3, 4, 5 ]
600 4 3 [ 1, 2, 3, 4, 5 ]
2400 5 4 [ 1, 2, 3, 4, 5 ]
12000

最后加上类型检测等

// 第三版
Array.prototype.fakeReduce = function fakeReduce(fn, base) {
  if (typeof fn !== "function") {
    throw new TypeError("arguments[0] is not a function");
  }
  let initialArr = this;
  let arr = initialArr.concat();

  if (base) arr.unshift(base);
  let index, newValue;

  while (arr.length > 1) {
    index = initialArr.length - arr.length + 1;
    newValue = fn.call(null, arr[0], arr[1], index, initialArr);

    arr.splice(0, 2, newValue); // 直接用 splice 实现替换
  }

  return newValue;
};

递归实现

简易版

const reduceHelper = (f, acc, arr) => {
  if (arr.length === 0) return acc
  const [head, ...tail] = arr
  return reduceHelper(f, f(acc, head), tail)
}

Array.prototype.fakeReduce = function (fn, initialValue) {
  const array = this
  return reduceHelper(fn, initialValue, array)
}

注:acc 即 accumulator, 累计回调的返回值。它是上一次调用回调时返回的累积值或 initialValue。

升级版

支持 cb 函数的全部参数

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)
}

重构非递归

Array.prototype.myReduce = function (cb, initialValue) {
  const array = this
  let acc = initialValue || array[0]
  const startIndex = initialValue ? 0 : 1

  for (let i = startIndex; i < array.length; i++) {
    const cur = array[i]
    acc = cb(acc, cur, i, array)
  }
  return acc
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

JavaScript 实现 reduce() 方法函数 的相关文章

  • Highstock highcharts 不规则数据的 x 尺度错误

    我有不规则的数据 我使用时图表绘制得很好高图表 function var chart new Highcharts Chart chart renderTo chart xAxis type datetime series name Vol
  • 使用 WMI 查找服务的依赖关系,然后区分依赖的服务和依赖的驱动程序

    MSDN 上有一个代码示例 它使用 WMI 枚举特定服务的所有依赖项 http msdn microsoft com en us library aa393673 v vs 85 aspx http msdn microsoft com e
  • 如何从 JavaScript 中计算 HTML 表格的渲染高度?

    调整窗口大小时 我需要知道表格有多大 以便我可以动态地很好地适应中间的所有其他内容 表格高度仅取决于动态加载的内容 如何在 JavaScript 中计算表格的渲染高度 您可以使用element offsetHeight https deve
  • JavaScript 对象引用缓存

    我们的代码库中有很多对象 它们的名称空间是为了避免冲突 Example App local view MyView 在我在代码库中看到的大多数地方 我们使用完整路径来获取对对象的引用 并且这在同一函数中重复多次 Example functi
  • 如何在光标下的所有元素上调用 mouseover?

    我有一个网络应用程序 每次单击时都会创建一个点 见下文 当我将鼠标悬停在一堆点上时 我希望光标下的每个点都会触发 mouseover 或 mouseenter 事件 然而 只有一个事件被触发 即堆栈 顶部 的点的事件 当鼠标移动到一堆多个点
  • 在javascript中使用“return !0”有什么意义吗?

    如果您转到谷歌结果页面并运行rwt toString 你会看到这个函数的返回调用是 return 0 我想不出任何理由为什么这不会总是如此true 这只是一个简写吗true 还是还有更多事情发生 总是如此 但下载需要 2 个字节 0是 2
  • 未捕获的类型错误:this.props.signinUser 不是一个函数(…)

    src actions index js import axios from axios const ROOT URL http localhost 3090 export function signinUser email passwor
  • HTML5 服务器端事件:EventSource 与包装的 WebSocket

    HTML5 服务器发送事件 SSE API 是否只是 HTML5 WebSocket 之上的受限制的 基于事件的 API 在我看来 一个EventSource只是一个WebSocket that Cannot send data 使用tex
  • 是否可以将反应组件导出为非反应项目中的函数

    有没有办法在非 React 项目中将 React 组件导出为函数并传入 props 作为函数的参数 我最近用 create react app 完成了一个 React 项目 现在我想将它用于其他非 React 项目 纯 Javascript
  • 在 Angular2 项目中集成 Treant-js

    我正在尝试在 Angular2 项目中使用 treant js 但我正在努力解决如何正确集成它的问题 我有一个工作正常的 JavaScript HTML 示例 我正在尝试在 Angular2 中工作 我创建了一个组件 从 npm 添加了 t
  • 引入 V8 后,Google Apps 脚本无法为其他用户完全执行

    我编写了一个脚本 得到了这里好心人的大力帮助 该脚本使用 Google Sheets 脚本复制 Google Drive 上的文件夹 和内容 它运行了很长一段时间 但后来我启用了 V8 引擎 现在已禁用 问题是 它仍然适用于我 也许还有其他
  • Postman - 如何计算 JSON 响应中特定对象的出现次数

    我是 JSON 和 Postman 的新手 我相信我正在尝试做一些非常简单的事情 我创建了一个 GET 请求 它将获得如下所示的 JSON 响应 在下面的例子中我想得到count响应中所有 IsArchived 属性 这些属性的数量因响应而
  • Meteor.js 登录事件

    因此 我对 Meteor 框架和 JavaScript 总体来说还很陌生 但我正在使用该框架开发一个小项目 以尝试让自己达到标准 基本上我正在开发一个微博客网站 目前 用户可以通过多种服务登录 fb google 等 我通过插入所需 url
  • 如何在粘贴时获取文本区域输入字段的新值?

    我发现当我尝试从文本区域字段读取值时onpaste调用函数时 我得到字段的旧值 粘贴操作之前的值 而不是新值 粘贴操作之后的值 以下是此行为的演示 http jsfiddle net qsDnr http jsfiddle net qsDn
  • 在声明组件选择器时添加指令 - Angular 7

    我正在学习 Angular 并通过单击按钮动态创建组件 我正在尝试使用 Angular Material 的拖放功能来拖动这些创建的组件以对它们进行排序 我的基本组件 html 中有以下代码 div style margin 20px di
  • ThreeJS无法加载Json文件

    首先 我已经读过这个问题 https stackoverflow com questions 17201888 three js exporter export object not working with jsonloader r58没
  • 使用 jQuery Tablesorter 操作后如何恢复当前页面?

    我正在使用 tablesorter 但无法找到有关插件 tablesorter 寻呼机的任何文档 问题是我有一个显示一些数据的表 并且在每一行中都有一个删除链接 该链接附加了要删除的元素的唯一标识符 显然 是否可以保存我正在删除的页面 然后
  • 如何根据所需表单输入的值更改 CSS 样式

    我想知道如何编写 javascript 来改变所需的表单元素的样式 如果它们有价值的话就改变它们 我想要做的是当所需的文本字段为空时 在它们周围有一个彩色边框 并在它们有值时删除边框样式 我想做的是编写一个 javascript 函数来检查
  • 如何在 JavaScript 中获取浮点数的小数位?

    我想要的是与 Number prototype toPrecision 几乎相反的 这意味着当我有数字时 它有多少位小数 例如 12 3456 getDecimals 4 对于任何想知道如何更快地完成此操作 无需转换为字符串 的人 这里有一
  • 使用 jquery 提供附加功能时菜单未正确对齐

    I need to make a mega menu similar to one as show in image below 到目前为止 我已经能够在某种程度上使其发挥作用 例如jsFiddle 在这里 http jsfiddle ne

随机推荐

  • 拯救者y7000笔记本VMware虚拟机打开蓝屏问题

    VMware虚拟机安装后开启虚拟机蓝屏检查步骤 一 必须确定你的CPU开启了虚拟化才行 要先开启虚拟化再去控制面板里设置 查看是否开启虚拟化方法 在win10系统最下方任务栏空白处 右键鼠标打开任务管理器 也可以点击快捷键Ctrl Alt
  • go语言实现邮件发送

    go语言实现邮件发送 安装相关的包 go get github com jordan wright email 代码篇 package main import fmt github com jordan wright email log n
  • Zotero软件与ChatGPT连用模版

    Zotero软件与ChatGPT连用模版 整体流程 其他配置 全文 AskPDF position 10 color 0EA293 trigger 本文 这篇文章 论文 You are a helpful assistant Context
  • 缓存一致性问题解决方案

    通常情况下 我们使用缓存的主要目的是为了提升查询的性能 大多数情况下 是这样使用缓存的 当数据库有数据更新时 在很长的一段时间内 决定于缓存的过期时间 用户请求从缓存中获取到的都可能是旧值 而非数据库的最新值 那么 该如何更新缓存呢 目前有
  • Python实现电商订单的数据分析

    一 数据信息 数据集 阿里云天池 数据来源 说明 本数据集共有104557条数据 共计11个字段 字段 id 序号 orderID 订单id userID 用户id goodsID 商品id orderAmount 订单总额 payment
  • 【Python刷题】P1

    Python刷题P1 第一题 第二题 第一题 1 给定一个非空的字符串 s 检查是否可以通过由它的一个子串重复多次构成 示例 1 输入 s abab 输出 true 解释 可由子串 ab 重复两次构成 示例 2 输入 s aba 输出 fa
  • C语言程序设计(谭浩强第五版)——例题

    C语言程序设计 谭浩强第五版 例题 第1章 程序设计和C语言 第2章 算法 程序的灵魂 第3章 最简单的C程序设计 顺序程序设计 第4章 选择结构程序设计 第5章 循环结构程序设计 第6章 利用数组处理批量数据 第7章 用函数实现模块化程序
  • 【算法基础】基于pytorch的BP神经网络算法代码+数据集

    实现功能 1 数据回归 基于多输入单输出数据集 2 代码最后使用matplotlib绘制了 训练次数 loss值 折线图进行训练结果展示 注 1 代码亲测可成功运行 2 以下代码通过cuda调用GPU训练 如果cuda报错则需检查GPU训练
  • python基础学习06_if条件判断(多重判断、嵌套、三目运算)

    一 条件语句 条件成立执行某些代码 条件不成立执行哪些代码 二 IF 条件判断 IF简单条件判断 多重判断 IF嵌套 三目运算符 1 if 简单条件判断 if if True print 条件成 执 的代码1 print 条件成 执 的代码
  • 微信小程序开发记录(一)弹出框和模态框介绍

    弹出框和模态框的简介和使用方法如下
  • k8s踩坑2——scheduler 、controller-manager Unhealthy

    查看master组件状态时出现错误 root k8s master kubectl get cs Warning v1 ComponentStatus is deprecated in v1 19 NAME STATUS MESSAGE E
  • ActiveMQ Redelivery Policy(消息重发策略)

    官方文档 http activemq apache org redelivery policy html 在事务控制里抛出异常 txManager会进行rollback处理 在activeMQ里 消息默认会redelivery到客户端6次
  • 教你用Mock实现复杂登陆接口下如何优雅的获取Token

    哈喽大家好 我是阿Q 背景 今天又双叒叕被抓壮丁了 被安排进了新的项目组进行任务开发 加入新项目后的第一件事 当然是先研究下同事的代码喽 在 学习 代码的过程中竟然惊奇的发现同事写了测试用例 对于一直使用PostMan来进行接口测试的我表示
  • MultipartFile中transferTo(File file)的路径问题

    今天看到layui的文件上传的控件 就尝试了一下 简单创建了一个SpringMVC项目 记得在配置文件中注入以下Bean
  • Pytorch 多GPU训练

    Pytorch 多GPU训练 目录 Pytorch 多GPU训练 1 导入库 2 指定GPU 2 1 单GPU声明 2 2 多GPU声明 3 数据放到GPU 4 把模型网络放到GPU 重要 torch nn DataParallel DP
  • Winform控件

    Winform控件 button 单选框 多选框 文本框 标签 显示图片控件 进度条属性 Halcon图片在PictureBox控件中显示 添加Halcon的引用 编写的代码如下 在PictureBox中显示图像变量HObject 先将之前
  • Android中关闭Activity

    1 知晓当前是哪个Activity 2 随时关闭所有Activity 有时我们可能会打开了很多个Activity 突然来个这样的需求 在某个页面可以关掉所有的Activity并退出程序 好吧 下面提供一个关闭所有Activity的方法 就是
  • 数据结构——堆

    堆的定义 如果有n个元素的序列 k0 k1 k2 kn 1 kn 当且仅当满足关系ki k2i且ki k2i 1 或 ki k2i且ki k2i 1 i 1 2 n 2 将满足ki k2i且ki k2i 1关系的堆称为小堆 将满足ki k2
  • Scratch模块介绍——运动(15个程序块3个数据块)

    今天 我给大家带来scratch的运动模块介绍 运动模块包含15个可执行程序块以及3个数据模块 首先 先介绍一下scratch Scratch是麻省理工学院开发的一款简易图形化编程工具 这个软件的开发团队称为 终身幼儿园团队 Lifelon
  • JavaScript 实现 reduce() 方法函数

    更新 增加递归实现的方法 更新 重构非递归实现的方法 思路 与之前两篇文章 map 的实现 filter 的实现 中的迭代方法不一样 reduce 是归并方法 reduce 接收两个参数 第一个参数是在每一项上调用的函数 该函数接收 4 个