百度360必应搜狗淘宝本站头条
当前位置:网站首页 > IT知识 > 正文

JS数组Reduce的妙用,收藏等于学会

liuian 2025-03-13 17:16 18 浏览


作者:前端发现者来源:前端发现

本文转载自微信公众号「前端发现」,作者前端发现者。转载本文请联系前端发现公众号。

说到处理数组的方法,想必大家都不陌生了,今天我们一起来学习下理数组常见场景下的方法。

首先来看看 reduce 方法可以传入哪些参数

function(pre,cur,index,arr)
  • pre:必需,初始值或计算结束后的返回值
  • cur:非必需,当前处理的元素
  • index:非必需,当前处理元素的索引
  • arr:非必需,当前元素所属的数组对象

直接看看

const list = [1,2,3,4,5] 
const result = list.reduce(function (pre, cur, index, arr) { 
  console.log('pre:' + pre, 'cur:' + cur, 'index:' + index) 
  return pre + cur 
}) 
console.log(result) 
 
// => pre:1 cur:2 index:1 
// => pre:3 cur:3 index:2 
// => pre:6 cur:4 index:3 
// => pre:10 cur:5 index:4 
// => 15

可以看到,第一轮pre的值是数组的第一个值,然后当前处理元素直接是元素的第二个数据,索引是数组的1。第二轮的pre就是第一次逻辑处理 return pre + cur 返回的结果(即3)。以此类推...共循环4轮。

再来看个相乘的处理逻辑的:

const list = [1,2,3,4,5] 
const result = list.reduce(function (pre, cur, index, arr) { 
  console.log('pre:' + pre, 'cur:' + cur, 'index:' + index) 
  return pre * cur 
}) 
console.log(result) 
 
// => pre:1 cur:2 index:1 
// => pre:2 cur:3 index:2 
// => pre:6 cur:4 index:3 
// => pre:24 cur:5 index:4 
// => 120

看着这么复杂,能举个再简单的例子吗?别问,问就是有!

const result = list.reduce((pre, cur) => pre + cur) 
console.log(result) // => 15

简单后再来个高级点的尝鲜下。

数组去重

将数组传输之前,我们先来了解下 reduce 的另外一个,即 initialValue。它是代表传递给函数的初始值,「可以理解为给pre设置了默认的值」。

const list = [1,1,3,5,5,7,9] 
let arr = list.reduce((pre,cur)=>{ 
  if(!pre.includes(cur)){ 
    return pre.concat(cur) 
  }else{ 
    return pre 
  } 
},[]) // => 给pre设置默认的空数组[] 
console.log(arr) // => [1, 3, 5, 7, 9]

可以看到list数组的长度为7,共循环7次(设置默认的空数组,导致cur第一轮是数组的第一个数据)。每循环一次就判断pre数组里存不存在当前循环的元素,若不存在则加入到pre数组去,否则就直接退出当前循环。

数组二维转一维

let arr = [1,2,[4, 6], [1, 6], [2, 2]] 
let newArr = arr.reduce((pre,cur)=>{ 
  return pre.concat(cur) 
},[]) 
console.log(newArr) // => [1, 2, 4, 6, 1, 6, 2, 2]

这里其实也就是利用了数组的 concat 方法,跟上面的使用也是大同小异,理顺一下就可以理解的了。

数组多维转一维

let arr = [1, 2, [4, 6], [1, 6, [3, 6]], [1, [3, 4, [1, 2]], [2, 2]]] 
const newArr = (arr) => { 
  return arr.reduce((pre, cur) => { 
    return pre.concat(Array.isArray(cur) ? newArr(cur) : cur) 
  }, []) 
} 
console.log(newArr(arr)) // => [1, 2, 4, 6, 1, 6, 3, 6, 1, 3, 4, 1, 2, 2, 2]

这里使用了 三目运算 、 concat 数据拼接 、递归 的思路完成。先判断当前处理的元素(有可能是数组)是不是数组(Array.isArray(cur)),如果是再次执行newArr,否则就直接处理当前元素,即将cur拼接进之前处理的数组中。

计算元素出现个数

讲解这个之前我们先来回忆下for...in的用法:

for...in 声明用于对数组或者对象的属性进行循环/迭代操作。

直接上

var arr = ['张三','李四','王五']     
for (let x in arr)   
{   
  console.log(x) 
  // => 张三 
  // => 李四 
  // => 王五 
}

可以看到当arr为数组时 x 相当于 for 循环的?? 标

那当arr为对象呢?

const obj = {   
  name: "张三",   
  age: 18,   
  height: "180"   
}   
for(let key in obj){   
  console.log(key)  
  // => name 
  // => age 
  // => height 
}

可以看到当循环的“对象”是对象时,循环的单项就是对象的属性。

所以我们可以根据这个特性来判断对象是否为数组/对象的元素/属性。

// 数组时判断下标 
let arr = ["a","b","2","3"]  
console.log("b" in arr) // => false 
console.log(2 in arr) // => true 
 
// 对象时判断属性 
let obj = {a:"a",b:"b",c:"2",d:"3"}  
console.log("b" in obj) // => true 
console.log(2 in obj) // => false

好的,回忆完这些知识,我们来看看怎么完成这个需求

let names = ['张三', '李四', '张三', '王五', '王五', '王五'] 
let total = names.reduce((pre,cur)=>{ 
  if(cur in pre){ 
    pre[cur]++ 
    console.log("判断为真:") 
    console.log(pre) 
  }else{ 
    pre[cur] = 1 
    console.log("判断为假:") 
    console.log(pre) 
  } 
  return pre 
},{}) 
console.log(total); // => {张三: 2, 李四: 1, 王五: 3}

首先先传入一个{}对象,说明初始的pre为{}。那么第一轮判断if的时候就变成 '张三' in {} 很明显此时判断条件是 false 。所以就执行 else 里面的逻辑后变成:{'张三':1}。第二轮时 李四 也是如此。当第三轮时再次遇到“张三”,此时对象是 {'张三':1,'李四':1} ,所以if判断是 true ,所以张三直接+1。来看看打印情况:

判断为假: 
// => {张三: 1} 
判断为假: 
// => {张三: 1, 李四: 1} 
判断为真: 
// => {张三: 2, 李四: 1} 
判断为假: 
// => {张三: 2, 李四: 1, 王五: 1} 
判断为真: 
// => {张三: 2, 李四: 1, 王五: 2} 
判断为真: 
// => {张三: 2, 李四: 1, 王五: 3}

属性求和

const list = [ 
  { 
    name: '张三', 
    age: 18 
  }, 
  { 
    name: '李四', 
    age: 20 
  }, 
  { 
    name: '王五', 
    age: 22 
  } 
] 
let total = list.reduce((pre, cur) => { 
  console.log(cur)  
  // => {name: '张三', age: 18} 
  // => {name: '李四', age: 20} 
  // => {name: '王五', age: 22} 
  return cur.age + pre 
}, 0) 
console.log(total) // => 60

如此是不是省了使用 map 去求和呢?更简便可以这么写:

let total = list.reduce((pre, cur) => cur.age + pre, 0)

相关推荐

教你把多个视频合并成一个视频的方法

一.情况介绍当你有一个m3u8文件和一个目录,目录中有连续的视频片段,这些片段可以连成一段完整的视频。m3u8文件打开后像这样:m3u8文件,可以理解为播放列表,里面是播放视频片段的顺序。视频片段像这...

零代码编程:用kimichat合并一个文件夹下的多个文件

一个文件夹里面有很多个srt字幕文件,如何借助kimichat来自动批量合并呢?在kimichat对话框中输入提示词:你是一个Python编程专家,完成如下的编程任务:这个文件夹:D:\downloa...

Java APT_java APT 生成代码

JavaAPT(AnnotationProcessingTool)是一种在Java编译阶段处理注解的工具。APT会在编译阶段扫描源代码中的注解,并根据这些注解生成代码、资源文件或其他输出,...

Unit Runtime:一键运行 AI 生成的代码,或许将成为你的复制 + 粘贴神器

在我们构建了UnitMesh架构之后,以及对应的demo之后,便着手于实现UnitMesh架构。于是,我们就继续开始UnitRuntime,以用于直接运行AI生成的代码。PS:...

挣脱臃肿的枷锁:为什么说Vert.x是Java开发者手中的一柄利剑?

如果你是一名Java开发者,那么你的职业生涯几乎无法避开Spring。它如同一位德高望重的老国王,统治着企业级应用开发的大片疆土。SpringBoot的约定大于配置、SpringCloud的微服务...

五年后,谷歌还在全力以赴发展 Kotlin

作者|FredericLardinois译者|Sambodhi策划|Tina自2017年谷歌I/O全球开发者大会上,谷歌首次宣布将Kotlin(JetBrains开发的Ja...

kotlin和java开发哪个好,优缺点对比

Kotlin和Java都是常见的编程语言,它们有各自的优缺点。Kotlin的优点:简洁:Kotlin程序相对于Java程序更简洁,可以减少代码量。安全:Kotlin在类型系统和空值安全...

移动端架构模式全景解析:从MVC到MVVM,如何选择最佳设计方案?

掌握不同架构模式的精髓,是构建可维护、可测试且高效移动应用的关键。在移动应用开发中,选择合适的软件架构模式对项目的可维护性、可测试性和团队协作效率至关重要。随着应用复杂度的增加,一个良好的架构能够帮助...

颜值非常高的XShell替代工具Termora,不一样的使用体验!

Termora是一款面向开发者和运维人员的跨平台SSH终端与文件管理工具,支持Windows、macOS及Linux系统,通过一体化界面简化远程服务器管理流程。其核心定位是解决多平台环境下远程连接、文...

预处理的底层原理和预处理编译运行异常的解决方案

若文章对您有帮助,欢迎关注程序员小迷。助您在编程路上越走越好![Mac-10.7.1LionIntel-based]Q:预处理到底干了什么事情?A:预处理,顾名思义,预先做的处理。源代码中...

为“架构”再建个模:如何用代码描述软件架构?

在架构治理平台ArchGuard中,为了实现对架构的治理,我们需要代码+模型描述所要处理的内容和数据。所以,在ArchGuard中,我们有了代码的模型、依赖的模型、变更的模型等,剩下的两个...

深度解析:Google Gemma 3n —— 移动优先的轻量多模态大模型

2025年6月,Google正式发布了Gemma3n,这是一款能够在2GB内存环境下运行的轻量级多模态大模型。它延续了Gemma家族的开源基因,同时在架构设计上大幅优化,目标是让...

比分网开发技术栈与功能详解_比分网有哪些

一、核心功能模块一个基本的比分网通常包含以下模块:首页/总览实时比分看板:滚动展示所有正在进行的比赛,包含比分、比赛时间、红黄牌等关键信息。热门赛事/焦点战:突出显示重要的、关注度高的比赛。赛事导航...

设计模式之-生成器_一键生成设计

一、【概念定义】——“分步构建复杂对象,隐藏创建细节”生成器模式(BuilderPattern):一种“分步构建型”创建型设计模式,它将一个复杂对象的构建与其表示分离,使得同样的构建过程可以创建...

构建第一个 Kotlin Android 应用_kotlin简介

第一步:安装AndroidStudio(推荐IDE)AndroidStudio是官方推荐的Android开发集成开发环境(IDE),内置对Kotlin的完整支持。1.下载And...