第9章 集合处理(数组、Map、Set)
1. 数组
1.1 创建数组
- 创建数组有两种基本方式:使用Array构造函数、使用数组字面量[]
// 通过构造函数
const subjects = new Array("Chinese", "Math", "English");
// 通过字面量
const fruits = ["apple", "banana", "pear"];
- JS的数组是对象
const subjects = new Array("Chinese", "Math", "English");
const fruits = ["apple", "banana", "pear"];
console.log(typeof subjects);
// object
console.log(typeof fruits);
// object
// 对访问不存在的数组索引,会返回undefined
console.log(fruits[4]);
// undefined
// 可以写入超出数组索引的数据项
fruits[4] = "orange";
// 但中间项会自动填充为undefined
console.log(fruits[3]);
// undefined
// length属性返回数组的大小
console.log(subjects.length);
// 3
// 可以手动修改length的值
subjects.length = 4;
// 将length改为比原有值大的数,数组会被扩展,新扩展的元素均为undefined
console.log(subjects);
// ["Chinese", "Math", "English", empty]
// 将length改为比原有值小的数,数组会被裁剪,多余数据项将被抛弃
subjects.length = 2;
console.log(subjects);
// ["Chinese", "Math"]
使用数组字面量创建数组优于数组构造函数(字面量只需要2个字符)
1.2 在数组两端添加删除元素
方法 | 作用 | 返回值 |
---|---|---|
push | 末尾添加元素 | 添加元素后数组的长度 |
pop | 末尾删除元素 | 被删除(弹出)的元素本身 |
unshift | 开头添加元素 | 添加元素后数组的长度 |
shift | 开头删除元素 | 被删除(弹出)的元素本身 |
const fruits = ["apple", "banana", "pear"];
let len = fruits.push("orange");
console.log(len);
// 4
console.log(fruits);
// ["apple", "banana", "pear", "orange"]
let lastFruit = fruits.pop();
console.log(lastFruit);
// orange
console.log(fruits);
// ["apple", "banana", "pear"]
len = fruits.unshift("orange");
console.log(len);
// 4
console.log(fruits);
// ["orange", "apple", "banana", "pear"]
let firstFruit = fruits.shift();
console.log(lastFruit);
// orange
console.log(fruits);
// ["apple", "banana", "pear"]
性能考虑:pop和push只影响数组最后一个元素,unshift和shift增减第一个元素,之后的每个元素的索引都需要调整。因此pop和push比unshift和shift快得多,非特殊情况下不建议使用unshift和shift。
1.3 在数组任意位置添加、删除元素
delete删除数组元素无效
const fruits = ["apple", "banana", "pear"];
delete fruits[1];
// 使用delete关键字并不能删除元素,只能删除对应索引的值,
console.log(fruits);
// ["apple", empty, "pear"]
// 被删除值的元素的值为undefined
console.log(fruits[1]);
// undefined
使用splice方法增、删、改元素
const fruits = ["apple", "banana", "pear"];
// 删除元素
// 两个参数,表示从索引2开始(索引>= 2),删除1个元素
// 返回值为被删去的元素(以数组的格式)
let elems = fruits.splice(2, 1);
console.log(fruits);
// ["apple", "banana"]
console.log(elems);
// ["pear"]
// 新增元素
// 三个以上参数,表示从索引1开始(索引>= 1),删除0个元素,新增后面的剩余参数
// 因为删除的元素个数为0,所以返回值是空数组
elems = fruits.splice(1, 0, "orange", "pear");
console.log(fruits);
// ["apple", "orange", "pear", "banana"]
console.log(elems);
// []
// 两者同时使用
// 三个以上参数,表示表示从索引1开始(索引>= 1),先删除3个元素,新增后面的剩余参数
// 返回值为被删去的元素(以数组的格式)
elems = fruits.splice(1, 3, "peach", "watermelon");
console.log(fruits);
// ["apple", "peach", "watermelon"]
console.log(elems);
// ["orange", "pear", "banana"]
1.4 数组的常用操作
数组遍历(forEach)
const fruits = ["apple", "banana", "pear"];
// for循环
for(let i = 0; i < fruits.length; i++) {
console.log(fruits[i]);
}
// forEach方法
fruits.forEach(fruit => {
console.log(fruit);
});
映射数组(map方法,对数组成员执行操作,并返回结果组成的新数组)
const students = [
{name: "Wango", age: 24},
{name: "Lily", age: 25},
{name: "Jack", age: 18},
];
// 使用forEach提取
let names = [];
students.forEach(student => {
names.push(student.name);
});
console.log(names);
// ["Wango", "Lily", "Jack"]
// 使用map方法提取,自动将返回值装入数组并返回
let age = students.map(student => student.age);
console.log(age);
// [24, 25, 18]
测试数组元素(every、some)
const students = [
{name: "Wango", age: 24},
{name: "Lily", age: 25},
{name: "Jack", age: 17},
];
// 使用every验证数组内是否每个元素都满足条件
// 回调函数对每个参数执行操作,当所有元素的回调结果都为true,every方法返回true
// 只要有一个为false,则every方法不再执行并返回false
let allStudentsAreAdult = students.every(student => student.age >= 18);
console.log(allStudentsAreAdult);
// fasle
// 使用some验证数组内是否有部分元素都满足条件
// 回调函数对每个参数执行操作,当所有元素的回调结果都为false,some方法返回false
// 只要有一个为true,则some方法不再执行并返回true
let someStudentsAreAdult = students.some(student => student.age >= 18);
console.log(someStudentsAreAdult);
// true
查找元素(find、filter、indexOf、lastIndexOf、findIndex)
const students = [
{name: "Wango", age: 24},
{name: "Lily", age: 25},
{name: "Jack", age: 17},
];
// 使用find查找单一元素,返回满足条件的第一个元素
// 没找到就返回undefined
let seventeen = students.find(student => student.age === 17);
console.log(seventeen.name);
// Jack
// 使用filter查找(过滤其他)元素,返回满足条件的所有元素(数组形式)
// 没找到就返回空数组
let adults = students.filter(student => student.age >= 18);
console.log(adults.map(adult => adult.name));
// ["Wango", "Lily"]
const apple = ["a", "p", "p", "l", "e"]
// indexOf接收一个元素值,返回顺序查找的特定元素的索引
// 没找到返回-1
let firstIndex = apple.indexOf("p");
console.log(firstIndex);
// 1
// lastIndexOf接收一个元素值,返回逆序查找的特定元素的索引
// 没找到返回-1
let lastIndex = apple.lastIndexOf("p");
console.log(lastIndex);
// 2
// 返回顺序查找的特定元素的索
// 接收回调函数作为参数,所以可以查找对象数组的索引
// 没找到返回-1
let idx = apple.findIndex(a => a === "l");
console.log(idx);
// 3
数组排序(sort)
// 如果回调函数的返回值小于0,第一个元素应该出现在第二个元素之前
// 如果回调函数返回0,两个元素的先后顺序保持不变
// 如果回调函数的返回值大于0,第一个元素应该出现在第二个元素之后
// 如果是纯数字数组,像下面这样写就行了
const nums = [5, 6, 7, 8, 1, 3, 5, 9, 6, 2,];
nums.sort((a, b) => a - b);
console.log(nums);
// [1, 2, 3, 5, 5, 6, 6, 7, 8, 9]
// 字符串没办法加减,只能比较大小,用if判断或者三元操作符判断一下然后返回
let chars = ["f", "a", "c", "u", "p"];
chars.sort((a, b) => {
return a > b ? 1 : a < b ? -1 : 0;
});
console.log(chars);
// ["a", "c", "f", "p", "u"]
// 如果需要从大到小排序就换一下返回值
合计数组元素(reduce)
const nums = [1, 2, 3, 4, 5, 6, 7, 8, 9];
// reduce方法接收一个回调函数
// 回调函数有两个必需参数, 第一个为累加器,用于存储计算结果,
// 第二个为当前正在处理的元素
let sum = nums.reduce((acc, cur) => acc + cur);
console.log(sum);
// 45
1.5 复用内置的数组函数(复用已经编写的代码)
<input type="text" id="first">
<input type="button" id="second">
<script>
// 创建一个可以复用数组方法的对象
const elems = {
length: 0, // 模拟数组长度
add: function(elem) {
// 复用数组代码
// 注意:原代码有返回值的,本代码也要有返回值
// 避免造成混乱
return Array.prototype.push.call(this, elem);
},
gather: function(id) {
return this.add(document.getElementById(id));
},
find: function(callback) {
return Array.prototype.find.call(this, callback);
}
}
elems.gather("first");
elems.gather("second");
// 数组代码会对length属性进行操作,
// 所以不需要再对length进行手动操作
console.log(elems.length);
// 2
const first = elems.find(elem => elem.id === "first");
console.log(first.type);
// text
</script>
2. Map
2.1 对象不是Map
访问对象未显式定义的属性,返回非预期
// 从字面看,对象符合字典的要求:键值对
// 但作为对象,其具有很多隐式属性
const student = {
wango: { id: 578, age: 24 },
lily: { id: 629, age: 25 },
jack: { id: 634, age: 22 }
}
// 对于未定义的属性,期望返回undefined,但
console.log(student.constructor);
// Object() { [native code] }
将对象的key映射为HTML节点造成key重复
const elems = {};
const first = document.getElementById("first");
const second = document.getElementById("second");
// HTML元素对象作为key,加入对象
elems[first] = "First Element";
elems[second] = "Second Element";
// 对象的key必须是字符串,非字符串类型会在后台静默调用toString方法
// 而相同类型元素的toString方法返回值相同,导致key重复
console.log(first.toString() === second.toString());
// true
// 前一个数据被后者替换
console.log(elems[first]);
// Second Element
console.log(elems[second]);
// Second Element
对象的原型有额外的继承属性、key仅支持字符串,所以通常不能使用对象作为map
2.2 创建map
<input type="text" id="first">
<input type="text" id="second">
<input type="text" id="third">
<script>
// 使用Map构造函数创建空map
const elemsMap = new Map();
const first = document.getElementById("first");
const second = document.getElementById("second");
const third = document.getElementById("third");
// 使用set方法创建映射,参数分别是:key, value
elemsMap.set(first, "First Element");
elemsMap.set(second, "Second Element");
// size属性为映射数量
console.log(elemsMap.size);
// 2
// get方法通过key获取value
console.log(elemsMap.get(first));
// First Element
console.log(elemsMap.get(second));
// Second Element
// has方法判断指定key是否存在
console.log(elemsMap.has(third));
// false
elemsMap.set(third, "Third Element");
// delete方法通过key删除映射
// 有这个key并删除成功返回true,没有这个key返回false
let a = elemsMap.delete(first);
console.log(a);
// true
console.log(elemsMap.size);
// 2
// clear方法清空map
elemsMap.clear();
console.log(elemsMap.size);
// 0
</script>
JS中两个对象的内容相同,但两个对象仍然不相等,所以可以作为map的key
const map = new Map();
let currentLocation = location.href;
// 创建两个相同内容的对象
const firstURL = new URL(currentLocation);
const secondURL = new URL(currentLocation);
map.set(firstURL, { "description": "First" });
map.set(secondURL, { "description": "Second" });
// 两个相同内容的对象都可以称为map的key
console.log(map.get(firstURL).description);
// First
console.log(map.get(secondURL).description);
// Second
2.3 遍历map(for-of)
const students = new Map();
students.set("Wango", 578);
students.set("Lily", 649);
students.set("Jack", 312);
// 遍历所有的students,没有元素有两个值:key和value
for(let student of students) {
console.log(student[0] + ": " + student[1]);
}
// Wango: 578
// Lily: 649
// Jack: 312
// 遍历所有的key
for(let name of students.keys()) {
console.log(name);
}
// Wango
// Lily
// Jack
// 遍历所有的value
for(let id of students.values()) {
console.log(id);
}
// 578
// 649
// 312
3. Set
3.1 使用对象模拟集合
// 用对象模拟集合
// 同样的问题,对象的key只能是字符串,这大大限制了模拟集合的能力
// 可以提供参数预填充集合
function Set(arr) {
this.data = {};
this.size = 0;
if (Array.isArray(arr)) {
// 数组去重
for (var i = 0; i < arr.length; i++) {
if (!this.has([arr[i]])) {
this.data[arr[i]] = arr[i];
this.size++;
}
}
}
}
Set.prototype.has = function (item) {
return typeof this.data[item] !== "undefined";
}
Set.prototype.add = function (item) {
if (!this.has(item)) {
this.data[item] = item;
this.size++;
}
return this.values();
}
Set.prototype.delete = function (item) {
if (this.has(item)) {
delete this.data[item];
this.size--;
return true;
}
return false;
}
Set.prototype.clear = function() {
var keys = this.keys();
this.size = 0;
for(var i = 0; i < keys.length; i++) {
delete this.data[keys[i]];
}
}
Set.prototype.keys = Set.prototype.values = function() {
return Object.getOwnPropertyNames(this.data);
}
Set.prototype.forEach = function(callback) {
var props = this.values();
for(var i = 0; i < props.length; i++) {
callback(props[i], props[i]);
}
}
3.2 创建Set
const students = new Set(["Wango", "Lily", "Jack"]);
console.log(students.has("Wango"));
// true
console.log(students.add("Jack"));
// Set(3) {"Wango", "Lily", "Jack"}
console.log(students.add("Tom"));
// Set(4) {"Wango", "Lily", "Jack", "Tom"}
console.log(students.delete("Wango"));
// true
console.log(students);
// Set(3) {"Lily", "Jack", "Tom"}
// 为了与map API保持一致,每个key和value的值相同
students.forEach((key, val) => {
console.log(key + ": " + val);
});
// Lily: Lily
// Jack: Jack
// Tom: Tom
for(let student of students) {
console.log(student);
}
// Lily
// Jack
// Tom
// 清空集合
students.clear();
3.2 并集、交集、差集
const firstGroup = new Set(["Wango", "Lily", "Tom"]);
const secondGroup = new Set(["Wango", "Lily", "Jack", "Mike"]);
// 并集,使用延展运算符打散两个集合,再合并成一个新的数组作为参数
// Set构造函数自动去重
const allStudents = new Set([...firstGroup, ...secondGroup]);
console.log(allStudents);
// {"Wango", "Lily", "Tom", "Jack", "Mike"}
// 交集,打散成数组后使用filter方法过滤掉secondGroup没有的元素
const sameStudents = new Set([...firstGroup].filter(stu => secondGroup.has(stu)));
console.log(sameStudents);
// {"Wango", "Lily"}
// 差集,只包含于集合A,不包含于集合B的元素,从A中过滤掉B中有的元素
const onlyInFirstGroup = new Set([...firstGroup].filter(stu => !secondGroup.has(stu)));
console.log(onlyInFirstGroup);
// {"Tom"}
第9章 集合处理(数组、Map、Set)的更多相关文章
- 第19章 集合框架(3)-Map接口
第19章 集合框架(3)-Map接口 1.Map接口概述 Map是一种映射关系,那么什么是映射关系呢? 映射的数学解释 设A,B是两个非空集合,如果存在一个法则,使得对A中的每一个元素a,按法则f,在 ...
- MyBatis传入集合 list 数组 map参数的写法
foreach的主要用在构建in条件中,它可以在SQL语句中进行迭代一个集合.foreach元素的属性主要有item,index,collection,open,separator,close.ite ...
- MyBatis传入参数为集合 list 数组 map写法
foreach的主要用在构建in条件中,它可以在SQL语句中进行迭代一个集合.foreach元素的属性主要有item,index,collection,open,separator,close.ite ...
- JAVA基础第五章-集合框架Map篇
业内经常说的一句话是不要重复造轮子,但是有时候,只有自己造一个轮子了,才会深刻明白什么样的轮子适合山路,什么样的轮子适合平地! 我将会持续更新java基础知识,欢迎关注. 往期章节: JAVA基础第一 ...
- 5、数组和集合--Collection、Map
一.数组:同一个类型数据的集合,其实他也是一个容器 1.数组的好处:可以自动给数组中的元素从0开始编号,方便操作这些数据 2.数组的定义: 在Java中常见: 格式1: 类型 [] 数组名 = ne ...
- 数组,集合,字符串,bean,map
//[字符串]转成[数组] String[] arr = "1,2,3,4,5,6".split(","); //[String数组]转成[Long数组] Lo ...
- JAVASE(十四) 集合: 数组和集合、Collection、Iterator、List、Set、Map
个人博客网:https://wushaopei.github.io/ (你想要这里多有) 1.数组和集合 1.1 内存中对数据进行存储和管理的“容器”:数组,集合 1.2 数组存储的特点和缺点 ...
- 集合遍历数组三种常用方式(Collecton和Map)
Collection集合遍历数组的三种方式: 迭代器 foreach(增强for循环) JDK1.8之后的新技术Lambda 迭代器: 方法:public Iterator inerator():获取 ...
- Java核心技术卷一基础技术-第13章-集合-读书笔记
第13章 集合 本章内容: * 集合接口 * 具体的集合 * 集合框架 * 算法 * 遗留的集合 13.1 集合接口 Enumeration接口提供了一种用于访问任意容器中各个元素的抽象机制. 13. ...
- 20190825 On Java8 第十二章 集合
第十二章 集合 java.util 库提供了一套相当完整的集合类(collection classes)来解决这个问题,其中基本的类型有 List . Set . Queue 和 Map. 不要在新代 ...
随机推荐
- CF484B Maximum Value
这个东西他一开始也是草稿 题解 我们考虑在值域上做,设值域为 \(m\) . 我们可以考虑数论分块,对于一对 \(a_i\) 和 \(a_j\) ,$\left \lfloor \frac{a_i}{ ...
- Canal监听mysql
安装mysql5.7,并开启binlog 安装mysql 开启binlog find / -name my.cnf 找到这个文件 添加几行 [mysqld] log-bin=mysql-bin # 开 ...
- 判断一个对象是否为空?怎么得到一个对象的第几个键名(key)?
var obj = {"微信":[],"qq":[]} console.log( Object.keys(obj) ) // ["微信",& ...
- Kubernetes弃用Docker后怎么办?
本文转自Rancher Labs 近期,Kubernetes在其最新的Changelog中宣布,自Kubernetes 1.20之后将弃用Docker作为容器运行时.这一消息在云原生领域激起了不小的水 ...
- [IOI1994]The Castle
开了博客之后一直没动今天水完题手痒想起这个就来水一篇陈年水题(雾 题目链接<< 题意:给一张n*m个格子的地图信息,求连通块个数以及最大连通块面积. 每个格子四个方向可以有墙,输入用一个十 ...
- SVN 使用教程 命令 visual studio 使用SVN
首先推荐大家一个应该是国内外最好的SVN仓库,不限私有,不限成员:https://svnbucket.com/ SVN官网 https://tortoisesvn.net/downloads.html ...
- VS中RDLC提示类型不一致
错误"基类包括字段"XXXXXXX",但其类型(Microsoft.Reporting.WebForms.ReportViewer)与控件(Microsoft.Repor ...
- H3C S5120V2-SI 交换机配置
连接终端线 可以看到开机信息 ......................................................................Done. System is ...
- 要多用Java帮助文档
从第一次接触Java到现在,大概两年了吧,间断断续续的学习.毕竟还在上课,其他课程也挺耗时间,但更多的还是自己不自律,很多时间都在玩. 平时用的有eclipse和IDEA,使用快捷方式有时看看源码,也 ...
- JAR-使用JAVA命令编译打包一个可执行jar包
一.开发一个演示项目 项目源代码开发 项目名称叫jar-package-example(其实只是一个文件夹, 用以将演示的所有文件夹和文件存放在其中, 没啥其它作用), 为了方便, 后文统一叫jar- ...