第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. 不要在新代 ...
 
随机推荐
- linux的Umask 为022 和027 都是什么意思?
			
用全部权限777去减这个数值 一.022表示默认创建新文件权限为755 也就是 rxwr-xr-x(所有者全部权限,属组读写,其它人读写) 二.027表示默认创建新文件权限为750 也就是rxwr- ...
 - 自搭建jetbrains系列ide授权服务器
			
1.下载 LicenseServer 地址:https://mega.nz/#!7B5UVY6b!Hae2ceTBPIrTowQN0sV9fQ5lGOKzGxas2ug02RZAdGU,里面有不同的服 ...
 - DVWA各等级文件上传漏洞
			
file upload 文件上传漏洞,攻击者可以通过上传木马获取服务器的webshell权限. 文件上传漏洞的利用是 够成功上传木马文件, 其次上传文件必须能够被执行, 最后就是上传文件的路径必须可知 ...
 - 用列表+for循环生成乘法口诀表
			
1 # 结合一下列表生成, 准备设计乘法表 2 # numlist = [1,2,3,4,5] 3 # [pow(i,3) for i in numlist] 4 # ## [1, 8, 27, 64 ...
 - Day1 字符串格式化
			
1.占位符方式:占位符: %d 整数 %012d 数字位数至少长度为12位,不足的前面加0填充. >>> 'Hello,%s,%012d' % ('a',12345678901234 ...
 - 四、LoadRunner11安装和破解
			
之前安装了LoadRunner12 社区版的,应为满足不了工作需求, 上网仔细查了教程下来LoadRunner11破解版 链接:https://pan.baidu.com/s/1dM8Lwf4p160 ...
 - 四、testNG.xml 简单介绍
			
TestNG定义了一套非常具体的术语描述测试. testng.xml testng.xml是一个以XML记录所有测试的文件.可以利用这个文件,跑同一个类或者多个不同类里面的测试用例. testng.x ...
 - Java IO流 FileOutputStream、FileInputStream的用法
			
FileOutputStream.FileInputStream的使用 FileOutputStream是OutputStream的继承类,它的主要功能就是向磁盘上写文件.FileOutputStre ...
 - get \post 接口代码及断言编写
			
post 请求接口 import requests import json url_path = "http://www.baidu.com" data = {"user ...
 - Java中的命名规范。
			
类:所有单词的首字母大写,如:TestJava. 方法:第1个单词的首字母小写,之后每个单词的首字母大写,如:getInfo(). 属性:第1个单词的首字母小写,之后每个单词的首字母大写,如:stud ...