幽沉谢世事,俯默窥唐虞。这篇文章主要讲述JavaScript 数据处理 - 列表篇相关的知识,希望能为你提供帮助。
程序中的常用数据集合无非两类,列表 (List) 和映射 (Map)。在 javascript 的语言基础中就提供了这两种集合结构的支持 —— 用数组 (Array) 表示列表,用直接对象 (Plain Object) 表示映射(属性键值对映射)。
今天我们只说数组。从 ??Array?
?? 类中提供的实例方法可以看出来,数组涵盖了一般的列表操作,增删改查俱全,更提供了 ??shift()/unshift()?
?? 和 ??push()/pop()?
? 这样的方法,使数组具有队列和栈的基本功能。除了日常的 CRUD 之外,最重要的就是对列表进行完全或部分遍历,拿到预期的结果,这些遍历操作包括
- 逐一遍历:?
?for?
??、??forEach()?
??、??map()?
? 等; - 筛选/过滤:?
?filter()?
??、??find()?
??、??findIndex()?
??、??indexOf()?
? 等; - 遍历计算(归约):?
?reduce()?
??、??some()?
??、??every()?
??、??includes()?
? 等。
?(el, index, array)?
??,分别表示当前处理的元素、当前元素的索引以及当前处理的数组(即原数组)。当然,这里说的是大多数,也有一些例外,比如 ??includes()?
?? 就不是这样,而 ??reduce?
?? 的处理函数会多一个表示中间结果的参数。具体情况不用多说,??查阅 MDN?? 即可。一、简单遍历大家都知道 ?
?for?
?? 语法在 javaScript 中除了基本的 ??for ( ;
;
)?
?? 之外,还包含了两种 for each 遍历。一种是 ??for ... in?
?? 用来遍历键/索引;另一种是 ??for ... of?
?? 用来遍历值/元素。两种 for each 结构都不能同时拿到键/索引和值/元素,而 ??forEach()?
?? 方法可以拿到,这是 ??forEach()?
?? 的便利所在。不过在 for each 结构中要终止循环,可以使用 ??break?
??,而在 ??forEach()?
?? 中要想终止循环只能通过 ??throw?
??。使用 ??throw?
?? 来终止循环需要在外面进行 ??try ... catch?
? 处理,不够灵活。举例:try
list.forEach(n =>
console.log(n);
if (n > = 3)throw undefined;
);
catch
console.log("The loop is broken");
如果没有 ?
?try ... catch?
??,里面的 ??throw?
? 会直接中断程序运行。当然,其实也有更简单的方法。注意到 ?
?some()?
?? 和 ??every()?
?? 这两个方法都是对数组进行遍历,直到遇到符合条件/不符合条件的元素。简单地说它们是根据处理函数的返回值来判断是否中断遍历。对于 ??some()?
?? 来说,是要找到一个符合条件的元素,处理函数如果返回 ??true?
??,就中断遍历;而 ??every()?
?? 正好相反,它是要判断每个元素都符合条件,所以只要遇到返回 ??false?
? 就会中断遍历。根据我们对一般 for 循环和 while 循环的理解,都是条件为真是进行循环,所以看起来 ?
?every()?
?? 更符合习惯。上面的示例用 ??every()?
? 改写:list.every(n =>
console.log(n);
return n < 3;
);
使用 ?
?some()?
?? 和 ??every()?
?? 特别需要注意一点:它不需要精确返回 boolean 类型的值,只需要判断真值 (truthy) 和 假值(falsy) 即可。 JavaScript 函数在没有显式返回值的情况下等同于 ??return undefined?
??,也就是返回假值,效果和 ??return false?
? 等同。关于 JavaScript 的假值,可以查阅 ??MDN - Falsy??。除了假值,都是真值。二、遍历映射有时候我们需要对一个数组进行遍历,根据其每个元素提供的信息,产生另一个数值和对象,而结果仍然放在一个数组中。前端开发中这种操作最常见的场景就是将从后端拿到的?模型数据?列表,处理成前端呈现需要的?视图数据?列表。常规操作是这样:
// 源数据
const source = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
// 创建目标数组容器
const target = [];
// 循环处理每一个源数据元素,并将结果添加到目标数组中
for (const n of source)
target.push( id: n, label: `label$n` );
// 消费目标数组
console.log(target);
【JavaScript 数据处理 - 列表篇】?
?map()?
?? 就是用来封装这样的遍历的,它可以用来处理一对一的元素数据映射。上例改用 ??map()?
? 只需要一句话代替循环:const source = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
const target = source.map(n => ( id: n, label: `label$n` ));
console.log(target);
除了减少语句之外,使用 ?
?map()?
? 还把原来的若干语句,变成了一个表达式,可以灵活地用于上下逻辑衔接。三、处理多层结构 - 展开 (flat 和 flatMap)展开,即 ?
?flat()?
? 操作可以把多维度的数组减少 1 个或多个维度。举例来说const source = [1, 2, [3, 4], [5, [6, 7], 8, 9], 10];
console.log(source.flat());
// [ 1, 2, 3, 4, 5, [ 6, 7 ], 8, 9, 10 ]
这个例子是个包含了三个维度(虽然不整齐)的数组,使用 ?
?flat()?
?? 减少了一个维度,其结果变成了两个维度。??flat()?
?? 可以通过参数指定展开的维度层数,这里只需要指定一个大于等于 ??2?
? 的值,它就能把所有元素全部展平到一个一维数组中:const source = [1, 2, [3, 4], [5, [6, 7], 8, 9], 10];
console.log(source.flat(10));
// [ 1, 2, 3, 4,5,6, 7, 8, 9, 10 ]
有了这个东西,我们在处理一些子项的时候就会比较方便。比如一个常见问题:
有一个二层的菜单数据,我想拿到所有菜单项列表,应该怎么办?数据如下
const data = https://www.songbingjia.com/android/[
label: "文件",
items: [
label: "打开", id: 11 ,
label: "保存", id: 12 ,
label: "关闭", id: 13
]
,
label: "帮助",
items: [
label: "查看帮助", id: 91 ,
label: "关于", id: 92
]
];
>
怎么办?毫无悬念应该是使用一个双层循环来处理。不过利用 ?
?map()?
?? 和 ??flat()?
?可以简化代码:const allItems = data.map(( items ) => items).flat();
//^^^^^^^^^
第一步 ?
?map()?
?? 把 ?? label, items ?
?? 类型的元素映射成为 ??[...items]?
? 这种形式的数组,映射结果是一个二维数组(示意):[
[...文件菜单项],
[...帮助菜单项]
]
再用 ?
?flat()?
?? 展平,就得到了 ??[...文件菜单项, ...帮助菜单项]?
?,也就是预期的结果。通常我们直接拿到二维数组来处理的情况极少,一般都需要先 ?
?map()?
?? 再 ??flat()?
??,所以 JavaScript 为这两个常用组合逻辑提供了 ??flatMap()?
?? 方法。要理解 ??flatMap()?
?? 的作用,就理解为先 ??map(...)?
?? 再 ??flat()?
? 即可。上面的示例可改为const allItems = data.flatMap(( items ) => items);
//^^^^^^^^
这里解决了一个两层结构的数据,如果是多层结构呢?多层结构不就是普通的树形结构,使用递归对所有子项进行 ?
?flatMap()?
? 处理即可。代码先不提供,请读者动动脑。四、过滤如果我们有一组数据,需要把其中符合某种条件的筛选出来使用,就会用到过滤,?
?filter()?
??。??filter()?
?? 接收一个用于判断的处理函数,并对每个元素使用该处理函数进行判断。如果该函数对某个元素的判断结果是真值,该元素会被保留;否则不会收录到结果中。??filter()?
? 的结果是原数组的子集。?
?filter()?
? 的用法很好理解,比如下面这个示例筛选出能被 3 整除的数:const a = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
const r = a.filter(n => n % 3 === 0);
// [ 3, 6, 9 ]
有两点需要强调:第一,如果所有元素都?不符合??条件,会得到一个空数组。既不是 ?
?null?
?? 也不是 ??undefined?
??,而是 ??[]?
?;第二,如果所有元素都?符合?条件,会得到一个包含所有元素的?新??数组。它与原数组进行 ?
?===?
?? 或 ??==?
?? 比较均会得到 ??false?
?。过滤虽然简单,但是要注意灵活运用。比如说需要统计某组数据中符合条件的个数,一般会想到遍历计数。但我们也可以先按指定条件过滤,再取结果数组的 ?
?length?
?。五、查找查找和过滤的区别在于:查找是找到一个符合条件的元素即可,而过滤是找到全部。从实现效果上来说,?
?arr.filter(fn)[0]?
?? 就可以达到查找效果。只不过 ?
推荐阅读
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 使用 KubeKey 快速离线部署 K8s 与 KubeSphere
- Server 2016部署AD活动目录安装搭建使用灾备恢复
- Docker——Docker网络模式
- 把握前端开发历史脉络
- v79.01 鸿蒙内核源码分析(用户态锁篇) | 如何使用快锁Futex(上) | 百篇博客分析OpenHarmony源码
- 一文搞懂 USB 设备端驱动框架
- 一分钟教你快速 搭建Vue脚手架(Vue-Cli)项目并整合ElementUI
- 超适合初学者的SpringBoot入门学习笔记,收藏起来慢慢看!
- 数据结构之跳跃链表