JS手写set与map

news2024/11/18 11:42:47

目录

  • set
    • add
    • has与equals
    • delete
    • 迭代器
    • 完整实现
  • map

set

set是一个没有重复元素的集合,事实上我们无法完全的使用js来模拟出set的全部功能,因为浏览器原生的set底层是使用c++实现,能直接访问内存,所以我们只能实现set的一部分功能
这里我们使用class来实现
具体关于class的部分可以看我这篇文章
class

add

首先我们有了一个mySet类,这个类会接受一个可迭代对象,我们需要判断这个可迭代对象是否真的可迭代
判断依据就是每个可迭代对象内都有一个[@@iterator]成员,并且这个成员还是一个方法

class mySet {
    constructor(iterator) {
        if (typeof iterator[Symbol.iterator] !== "function") throw Error(`${iterator}不是可迭代对象`)
    }
}

接下来我们定义一个数组来存储相关数据,在构造方法内遍历这个可迭代对象,将其中的内容通过add方法填入数组中

class mySet {
    #data = []
    constructor(iterator) {
        if (typeof iterator[Symbol.iterator] !== "function") throw Error(`${iterator}不是可迭代对象`)
        for (const item of iterator) {
            this.add(item)
        }
    }
    add(item) {
    }
}

has与equals

set与普通数组最大不同的一点就在于set内的元素是不会重复的,所以我们在向数组中放入元素时需要提前判断一下该元素是否已在数组中
我们可以通过调用has方法来判断此元素是否在数组中

class mySet {
    #data = []
    constructor(iterator) {
        if (typeof iterator[Symbol.iterator] !== "function") throw Error(`${iterator}不是可迭代对象`)
        for (const item of iterator) {
            this.add(item)
        }
    }
    add(item) {
        if (!this.has(item)) this.#data.push(item)
    }
    has(item) {
    }
}

现在问题转变成了has方法怎么实现
我们可以遍历data数组,将传入的值通过Object.is来判断是否相同,如果相同就返回true,全部遍历完都没有返回的话就返回false
但是Object.is有一个问题,在Object.is中+0和-0被认为是两个不同的东西,所以我们需要将Object.is再封装一层,这个封装可有可无,但set内部是针对Object.is封装了的

class mySet {
    add(item) {
        if (!this.has(item)) this.#data.push(item)
    }
    has(item) {
        for (const iterator of this.#data) {
            if (mySet.#equal(item, iterator)) {
                return true
            }
        }
        return false;
    }
    static #equal(item1, item2) {
        if (item1 === 0 && item2 === 0) {
            return false
        }
        return Object.is(item1, item2)
    }
}

我们封装了一个静态方法equal用来比较传入的两个值是否相等

delete

接下来我们就需要实现delete方法

class mySet {
    delete(item) {
        for (const iterator of this.#data) {
            if (mySet.#equal(item, iterator)) {
                this.#data.splice(i, 1)
            }
        }
    }
}

迭代器

我们知道set是能直接被forof遍历的,这说明了set内部是实现了迭代器,
迭代器(iterator),可以把它当做是一个接口,用户可以使用该接口来遍历数据而不需关心数据内部的实现细节
具体关于迭代器的部分可以看我这篇博客
迭代器与生成器

class mySet {
    *[Symbol.iterator]() {
        yield* Object.entries(this.#data)
    }
}

完整实现

我们接下来还有一些成员没有实现,如size,clear,forEach成员,具体代码如下

class mySet {
    #data = []
    constructor(iterator) {
        if (typeof iterator[Symbol.iterator] !== "function") throw Error(`${iterator}不是可迭代对象`)
        for (const item of iterator) {
            this.add(item)
        }
    }
    add(item) {
        if (!this.has(item)) this.#data.push(item)
    }
    has(item) {
        for (const iterator of this.#data) {
            if (mySet.#equal(item, iterator)) {
                return true
            }
        }
        return false;
    }
    delete(item) {
        for (const iterator of this.#data) {
            if (mySet.#equal(item, iterator)) {
                this.#data.splice(i, 1)
            }
        }
    }
    clear() {
        this.#data.length = 0
    }
    forEach(fn) {
        for (let i = 0; i < this.#data.length; i++) {
            const item = this.#data[i]
            fn(item, i, this)
        }
    }
    *[Symbol.iterator]() {
        yield* Object.entries(this.#data)
    }
    get size() {
        return this.#data.length
    }
    static #equal(item1, item2) {
        if (item1 === 0 && item2 === 0) {
            return false
        }
        return Object.is(item1, item2)
    }
}

测试代码

const myset = new mySet([12, 3, 56, 56, 5, 5]);
console.log(myset)
const set = new Set([1, 2, 3, 4, 5])
for (const iterator of myset) {
    console.log(iterator)
}

结果
结果

map

map和set类似,这里只描述map与set的不同点

  1. 在set中我们只需要针对constructor中传入的对象进行判断,而在map中我们需要针对对象中的各个元素进行判断是否可迭代

    constructor(iterator) {
        if (typeof iterator[Symbol.iterator] !== "function") throw Error(`${iterator}不是可迭代对象`)
        for (const item of iterator) {
            if (typeof item[Symbol.iterator] !== "function") {
                throw Error(`${item}不是可迭代对象`)
            }
            const iterator = item[Symbol.iterator]();
            const key = iterator.next().value;
            const value = iterator.next().value;
            this.set({
                key,
                value
            })
        }
    }
    

    如果符合条件的话就将它的第一项拿出作为key,第二项拿出作为value,因为不能确定每个元素都是什么类型,所以只能通过迭代器去拿到相应的值

  2. 在set中我们需要大量的遍历,我们可以封装一个函数用来获取data中的元素

    #getObj(key) {
    
        for (const item of this.#data) {
            if (myMap.#equal(item.key, key)) {
                return item
            }
        }
        return undefined
    }
    

    这个函数传入一个key,如果有的话返回该元素,没有的话就返回undefined,之后其他方法如果需要遍历数组来拿到元素,就可以使用这个方法

  3. 在构建迭代器的时候我们需要知道数组里存的元素是对象,在迭代的时候我们需要将它变为二维数组

    *[Symbol.iterator]() {
        for (const item of this.#data) {
            yield [item.key, item.value]
        }
    }
    

最终代码

class myMap {
    #data = []
    constructor(iterator) {
        if (typeof iterator[Symbol.iterator] !== "function") throw Error(`${iterator}不是可迭代对象`)
        for (const item of iterator) {
            if (typeof item[Symbol.iterator] !== "function") {
                throw Error(`${item}不是可迭代对象`)
            }
            const iterator = item[Symbol.iterator]();
            const key = iterator.next().value;
            const value = iterator.next().value;
            this.set({
                key,
                value
            })
        }
    }
    set(item) {
        const obj = this.#getObj(item.key)
        if (this.has(item.key)) {
            obj.value = item.value
        } else {
            this.#data.push(item)
        }
    }
    has(key) {
        const item = this.#getObj(key)
        return item ? true : false
    }
    get(key) {
        return this.#getObj(key);
    }
    delete(key) {
        this.#data = this.#data.filter((value) => {
            if (value.key !== key) return value
        })
    }
    forEach(fn) {
        for (const item of this.#data) {
            fn(item.value, item.key, this)
        }
    }
    #getObj(key) {
        for (const item of this.#data) {
            if (myMap.#equal(item.key, key)) {
                return item
            }
        }
        return undefined
    }
    static #equal(item1, item2) {
        if (item1 === 0 && item2 === 0) {
            return false
        }
        return Object.is(item1, item2)
    }
    *[Symbol.iterator]() {
        for (const item of this.#data) {
            yield [item.key, item.value]
        }
    }
}

测试代码

const mymap = new myMap([[1, 'a'], [{ name: "a" }, 2], [null, 3], [undefined, 4], [5, null]])
console.log(mymap.get(null))
mymap.delete(undefined)
for (const item of mymap) {
    console.log(item)
}

结果

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1624989.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Django与mysqlclient链接不成功

先检查自己的python是什么版本&#xff0c;是64位还是32位&#xff0c;这个自己去网上查。 我的是32位的&#xff0c;因为直接pip下载不了&#xff0c;网上也没有32位的whl&#xff0c;所以卸载重装一个64位的3.9.6的python 网上直接搜mysqlclient&#xff0c;找到对应py39也…

WPF2 样式布局

样式布局 WPF中的各类控件元素, 都可以自由的设置其样式。 诸如: 字体(FontFamily) 字体大小(FontSize) 背景颜色(Background) 字体颜色(Foreground) 边距(Margin) 水平位置(HorizontalAlignment) 垂直位置(VerticalAlignment) 等等。 而样式则是组织和重用以上的重要工具。…

ray.tune调参学习笔记1:超参数优化器tuner设置

最近研究中学习使用python的ray.tune进行神经网络调参。在这里记录学习过程中的收获&#xff0c;希望能够帮助到有同样需求的人。学习过程主要参考ray官网文档&#xff0c;但由于笔者使用的ray为2.2.0版本&#xff0c;而官方文档为更高级版本&#xff0c;笔者代码和官方文档代码…

Python实现本地视频/音频播放器

Python实现本地视频/音频播放器 在Python中&#xff0c;有几个库可以用于视频播放&#xff0c;但是没有一个库是完美的&#xff0c;因为它们可能依赖于外部软件或有一些限制。 先看介绍用Python实现本地视频播放器&#xff0c;再介绍用Python实现本地音乐播放器。 Python实现…

【C 数据结构】图

文章目录 【 1. 基本原理 】1.1 无向图1.2 有向图1.3 基本知识 【 2. 图的存储结构 】2.1 完全图2.2 稀疏图和稠密图2.3 连通图2.3.1 (普通)连通图连通图 - 无向图非连通图 的 连通分量 2.3.2 强连通图强连通图 - 有向图非强连通有向图 的 强连通分量 2.3.3 生成树 - 连通图2.3…

重仓比特币

作者&#xff1a;Arthur Hayes Co-Founder of 100x. 编译&#xff1a;liam ccvalue (下文中表达的任何观点均为作者的个人观点&#xff0c;不应作为投资决策的依据&#xff0c;也不应被视为参与投资交易的建议或意见&#xff09;。 我们中断牛市常规节目&#xff0c;为您播报这…

【研发管理】产品经理知识体系-产品创新中的市场调研

导读&#xff1a;在产品创新过程中&#xff0c;市场调研的重要性不言而喻。它不仅是产品创新的起点&#xff0c;也是确保产品成功推向市场的关键步骤。对于产品经理系统学习和掌握产品创新中的市场调研相关知识体系十分重要。 目录 概述&#xff1a;市场调研重要性 1、相关概…

华为数字化转型与数据管理实践介绍(附PPT下载)

华为作为全球领先的信息与通信技术&#xff08;ICT&#xff09;解决方案提供商&#xff0c;在数字化转型和数据管理领域拥有丰富的实践经验和技术积累。其数字化转型解决方案旨在帮助企业通过采用最新的ICT技术&#xff0c;实现业务流程、组织结构和文化的全面数字化&#xff0…

Kafka 3.x.x 入门到精通(03)——对标尚硅谷Kafka教程

Kafka 3.x.x 入门到精通&#xff08;03&#xff09;——对标尚硅谷Kafka教程 2. Kafka基础2.1 集群部署2.2 集群启动2.3 创建主题2.4 生产消息2.4.1 生产消息的基本步骤2.4.2 生产消息的基本代码2.4.3 发送消息2.4.3.1 拦截器2.4.3.1.1 增加拦截器类2.4.3.1.2 配置拦截器 2.4.3…

Spring Bean 的生命周期与作用域解析及实战

引言 在Spring框架中&#xff0c;Bean是构成应用的核心组件&#xff0c;它们负责执行应用中的业务逻辑。理解Spring Bean的生命周期和作用域对于开发高效、稳定的Spring应用至关重要。本文将详细解析Spring Bean的生命周期和作用域&#xff0c;并通过实战案例加深理解。 一、…

使用FunASR处理语音识别

FunASR是阿里的一个语音识别工具&#xff0c;比SpeechRecognition功能多安装也很简单&#xff1b; 官方介绍&#xff1a;FunASR是一个基础语音识别工具包&#xff0c;提供多种功能&#xff0c;包括语音识别&#xff08;ASR&#xff09;、语音端点检测&#xff08;VAD&#xff…

微软ML Copilot框架释放机器学习能力

摘要&#xff1a;大模型席卷而来&#xff0c;通过大量算法模型训练推理&#xff0c;能根据人类输入指令产生图文&#xff0c;其背后是大量深度神经网络模型在做运算&#xff0c;这一过程称之为机器学习&#xff0c;本文从微软语言大模型出发&#xff0c;详解利用大型语言模型&a…

Java设计模式:使用责任链模式和状态模式优化‘审批流程‘

Java设计模式&#xff1a;使用责任链模式和状态模式优化审批流程 摘要引言 需求流程图正文内容&#x1f4d0; 基本概念介绍 功能实现示例1:设计模式&#xff1a;责任链模式方法&#xff1a;好处&#xff1a; 示例2:设计模式&#xff1a;责任链模式方法和操作流程&#xff1a;好…

【stm32】swjtu西南交大嵌入式实验三 外部中断实验:按键中断

实验内容&#xff1a; 1、编写程序&#xff0c;设置主程序&#xff1a;跑马灯以 0.2s 的速度旋转&#xff1b;将 KB1 设置为外部中断&#xff0c;下 降沿触发&#xff0c;按下 KB1 则全彩灯的 R 灯闪烁 5 次。编译、下载程序到开发板&#xff0c;观察实 验现象&#xff1b;按下…

torch.cuda.is_avaliable()在命令行里是true,pycharm是false【省流:换Pycharm】

我的问题&#xff1a; 1、torch.cuda.is_avaliable()在命令行里是true&#xff0c;但是pycharm是false 2、pycharm选择pytorch所在的解释器&#xff0c;加载失败。 3、pytorch所在的解释器加载成功&#xff0c;但是里边的torch包莫名消失。 解决方法&#xff1a; 在调试了很…

Sping源码(七)—context: component-scan标签如何扫描、加载Bean

序言 简单回顾一下。上一篇文章介绍了从xml文件context component-scan标签的加载流程到ConfigurationClassPostProcessor的创建流程。 本篇会深入了解context component-scan标签底层做了些什么。 component-scan 早期使用Spring进行开发时&#xff0c;很多时候都是注解 标…

免费好用各科目的刷题软件

一 、前言 刷题&#xff0c;即通过大量做题来提高解题能力和考试成绩的行为&#xff0c;主要有以下几个好处&#xff1a; 1. 熟悉题型和考试格式 通过刷题&#xff0c;可以对考试中可能出现的题型和格式有更深入的了解&#xff0c;有助于在实际考试中快速识别题目类型和解题…

Java使用IText根据pdf模板创建pdf文件

1.导包 <dependency><groupId>com.itextpdf</groupId><artifactId>itextpdf</artifactId><version>5.5.10</version></dependency><dependency><groupId>com.itextpdf</groupId><artifactId>itext-as…

mdk输出本语句在源程序中行数以及函数名称

代码如下&#xff1a; /* USER CODE BEGIN WHILE */while (1){printf("anlog uart1 test 2024-4-16\r\n");printf("[%s] %d\r\n",__func__,__LINE__);printf("[%s] %d",__func__,__LINE__);HAL_Delay(200);/* USER CODE END WHILE *//* USER COD…

MyBatis源码之MyBatis中SQL语句执行过程

MyBatis源码之MyBatis中SQL语句执行过程 SQL执行入口 我们在使用MyBatis编程时有两种方式&#xff1a; 方式一代码如下&#xff1a; SqlSession sqlSession sqlSessionFactory.openSession(); List<Student> studentList sqlSession.selectList("com.sjdwz.da…