Javascript数组研究06_手写实现_shift_slice_some_sort_splice

26 Array.shift()

26.1 基本介绍

shift() 方法从数组中删除第一个元素,并返回该元素的值。此方法会更改数组的长度。

array.shift()

输入参数:无。

输出:被移除的第一个元素。如果数组为空,则返回 undefined

注意事项

  • shift() 方法会修改原数组,是一个修改方法
  • 删除第一个元素后,后续元素的索引会依次减一。
26.2 手写实现
MyArray.prototype.shift = function() {
    if (this.length === 0) {
        return undefined;
    }

    let firstElement = this[0];

    for (let i = 1; i < this.length; i++) {
        // 整个数组索引1到length - 1 需要向前移动
        if (i in this) {
            // 空槽不可以以这种方式移动,会变成undefined
            this[i - 1] = this[i];
        } else {
            // 正确处理空槽德移动方式,delete对象德属性
            delete this[i - 1];
        }
    }

    // 删除最后一个元素,减少length
    delete this[this.length - 1];
    this.length--;

    return firstElement;
};

// 测试用例
let arr = new MyArray(1, 2, 3).concat([,,4]);
let shiftedElement = arr.shift();
console.log(shiftedElement); // 1
console.log(arr); // [2, 3]

let emptyArr = new MyArray();
let shiftedFromEmpty = emptyArr.shift();
console.log(shiftedFromEmpty); // undefined
console.log(emptyArr); // []

难点总结

  • 处理稀疏数组:需要正确处理空槽,避免错误地复制undefined,需要使用delete this[i - 1]相当于移动了空槽。
  • 更新 length 属性:需要手动更新数组的 length 属性,删除delete数组德最后一个元素。

27 Array.slice()

27.1 基本介绍

slice() 方法返回一个从开始到结束(不包括结束)选择的数组的一部分浅拷贝到一个新数组对象中。原始数组不会被修改。

array.slice()
array.slice(start)
array.slice(start, end)

输入参数

  • start(可选):开始提取的索引,默认为 0。负索引表示从末尾开始计算。
  • end(可选):结束提取的索引(不包括该索引),默认为数组长度。负索引表示从末尾开始计算。

输出:一个新的数组,包含从 startend(不包括 end)的元素。

注意事项

  • slice() 方法不会修改原数组,是一个复制方法
  • 对于稀疏数组,返回的新数组也会保留对应位置的空槽。
  • slice() 方法是通用方法,可用于类数组对象。
27.2 手写实现
MyArray.prototype.slice = function(start = 0, end = this.length) {
    let length = this.length >>> 0;

    // 处理负索引
    let relativeStart = start < 0 ? Math.max(length + start, 0) : Math.min(start, length);
    let relativeEnd = end < 0 ? Math.max(length + end, 0) : Math.min(end, length);

    let count = Math.max(relativeEnd - relativeStart, 0);
    let result = new MyArray();

    for (let i = 0; i < count; i++) {
        let index = relativeStart + i;
        // 只有在不是空槽的情况下才会赋值
        // 所以空槽其实就是不存在的对象属性
        if (index in this) {
            result[i] = this[index];
        }
    }

    // 不要忘记处理结果的数组长度
    result.length = count;
    return result;
};

// 测试用例
let arr_1 = new MyArray(1, 2, 3, 4, 5);
let slicedArr = arr_1.slice(1, 3);
console.log(slicedArr); // [2, 3]

let arr_2 = new MyArray().concat([1, , 3, , 5]);
let slicedArr2 = arr_2.slice(1, 4);
console.log(slicedArr2); // [empty, 3, empty]
console.log(slicedArr2.length); // 3

难点总结

  • 处理稀疏数组:返回的新数组需要保留空槽位置,空槽实际上就是不存在数组列表对象属性。
  • 计算长度:确保新数组的 length 属性设置正确。

28 Array.some()

28.1 基本介绍

some() 方法测试数组中是否至少有一个元素通过了由提供的函数实现的测试。如果在数组中找到一个元素使得提供的函数返回 true,则返回 true;否则返回 false。它不会修改数组。

array.some(callbackFn)
array.some(callbackFn, thisArg)

输入参数

  • callbackFn(element, index, array):用于测试每个元素的函数。
  • thisArg(可选):执行 callbackFn 时使用的 this 值。

输出:如果数组中至少有一个元素通过了测试,则返回 true;否则返回 false

注意事项

  • some() 方法不会修改原数组。
  • 一旦找到使 callbackFn 返回真值的元素,方法将立即返回 true,不会继续遍历。
  • 空数组会返回false
  • 对于稀疏数组,some() 会跳过空槽。
  • some() 方法是通用方法
28.2 手写实现
MyArray.prototype.some = function(callbackFn, thisArg) {
    if (typeof callbackFn !== 'function') {
        throw new TypeError(callbackFn + ' is not a function');
    }

    for (let i = 0; i < this.length; i++) {
        if (!(i in this)) continue; // 跳过空槽
        // 使用.call调用函数,绑定thisArg
        if (callbackFn.call(thisArg, this[i], i, this)) {
            return true;
        }
    }
    return false;
};

// 测试用例
let arr_3 = new MyArray(1, 2, 3, 4);
let hasEven = arr_3.some(x => x % 2 === 0);
console.log(hasEven); // true

let arr_4 = new MyArray().concat([1, , 3]);
let hasUndefined = arr_4.some(x => x === undefined);
console.log(hasUndefined); // false

难点总结

  • 提前终止遍历:一旦 callbackFn 返回真值,some() 方法立即返回 true
  • 处理稀疏数组:跳过空槽,不调用 callbackFn
  • 正确绑定 thisArg:在调用 callbackFn 时需要正确绑定 thisArg

29 Array.sort()

29.1 基本介绍

sort() 方法用原地算法对数组的元素进行排序,并返回数组。默认排序顺序是在将元素转换为字符串,然后比较它们的 UTF-16 代码单元值序列时构建的。

array.sort()
array.sort(compareFunction)

输入参数

  • compareFunction(a, b)(可选):用于定义排序顺序的函数。返回值为负数表示 ab 之前,正数表示 ab 之后,零表示相等。

输出:排序后的数组。原数组已被修改。

注意事项

  • sort() 方法会修改原数组,是一个修改方法
  • 如果没有提供 compareFunction,元素会被转换为字符串,然后按字典顺序排序。
  • 对于undefined,会放在数组的结尾
  • 对于稀疏数组,sort() 会将空槽排在所有 undefined的后面
29.2 手写实现
MyArray.prototype.sort = function(compareFunction) {
    let length = this.length >>> 0;

    // 分离数组元素
    let elements = [];
    let undefinedElements = [];
    let holesCount = 0;

    for (let i = 0; i < length; i++) {
        if (i in this) {
            let value = this[i];
            if (value === undefined) {
                undefinedElements.push(value);
            } else {
                elements.push(value);
            }
        } else {
            holesCount++; // 统计空槽数量
        }
    }

    // 默认比较函数
    if (typeof compareFunction !== 'function') {
        compareFunction = function(a, b) {
            let strA = String(a);
            let strB = String(b);
            if (strA < strB) return -1;
            if (strA > strB) return 1;
            return 0;
        };
    }

    // 手写排序算法(快速排序)
    function quickSort(arr, left, right) {
        if (left >= right) return;

        let pivotIndex = partition(arr, left, right);
        quickSort(arr, left, pivotIndex - 1);
        quickSort(arr, pivotIndex + 1, right);
    }

    function partition(arr, left, right) {
        let pivot = arr[right];
        let i = left - 1;
        for (let j = left; j < right; j++) {
            if (compareFunction(arr[j], pivot) <= 0) {
                i++;
                [arr[i], arr[j]] = [arr[j], arr[i]];
            }
        }
        [arr[i + 1], arr[right]] = [arr[right], arr[i + 1]];
        return i + 1;
    }

    quickSort(elements, 0, elements.length - 1);

    // 清空原数组
    for (let i = 0; i < length; i++) {
        delete this[i];
    }

    // 重新赋值排序后的元素
    let index = 0;
    for (let i = 0; i < elements.length; i++) {
        this[index++] = elements[i];
    }

    // 添加 undefined 元素
    for (let i = 0; i < undefinedElements.length; i++) {
        this[index++] = undefined;
    }

    // 保留空槽(holes)
    this.length = index + holesCount;

    return this;
};

// 测试用例 1:数字排序(未提供 compareFunction)
let arr_5 = new MyArray(3, undefined, 80, 9, undefined, 10);
arr_5[10] = 100; // 在索引 10 处创建一个元素,索引 6-9 为空槽
arr_5.sort();
console.log(arr_5); // [10, 100, 3, 80, 9, undefined, undefined, <4 empty items>]

// 测试用例 2:字符串排序(未提供 compareFunction)
let arr_6 = new MyArray().concat(['banana', undefined, 'apple', , 'cherry']);
arr_6.sort();
console.log(arr_6); // ['apple', 'banana', 'cherry', undefined, <1 empty item>]

// 测试用例 3:使用自定义比较函数(不区分大小写)
let arr_7 = new MyArray('Banana', 'apple', 'Cherry', undefined);
arr_7.sort((a, b) => a.toLowerCase().localeCompare(b.toLowerCase()));
console.log(arr_7); // ['apple', 'Banana', 'Cherry', undefined]

// 测试用例 4:稀疏数组
let arr_8 = new MyArray();
arr_8[2] = 'c';
arr_8[5] = 'a';
arr_8[7] = undefined;
arr_8[9] = 'b';
arr_8.sort();
console.log(arr_8); // ['a', 'b', 'c', undefined, <6 empty items>]

难点总结

  • 实现排序算法:实现一个完整的排序算法(如快速排序)较为复杂。
  • 处理 compareFunction:需要正确处理用户提供的比较函数。
  • 处理稀疏数组及undefined:需要正确处理空槽和undefined,排序前将其转换为密集数组。
  • 原地修改:需要确保对原数组进行修改,而不是返回新数组。

30 Array.splice()

30.1 基本介绍

splice() 方法通过删除或替换现有元素或者原地添加新的元素来修改数组,并以数组形式返回被修改的内容。此方法会改变原数组。

array.splice(start)
array.splice(start, deleteCount)
array.splice(start, deleteCount, item1, item2, ...)

输入参数

  • start:指定修改的开始位置(从 0 开始计数)。如果超出数组长度,则从数组末尾开始添加内容。如果为负值,表示从数组末尾开始计数。
  • deleteCount(可选):整数,表示要移除的数组元素的个数。
    • 如果 deleteCount 为 0,则不删除元素。
    • 如果 deleteCount 大于剩余元素的数量,则删除到数组末尾。
    • 如果未指定 deleteCount,则删除从 start 开始的所有元素。
  • item1, item2, ...(可选):要添加进数组的元素,从 start 位置开始插入。

输出:一个由被删除的元素组成的数组。如果没有删除元素,则返回空数组。

注意事项

  • splice() 方法会修改原数组,是一个修改方法
  • 如果仅添加元素,不删除,则 deleteCount 应为 0。
  • 如果仅删除元素,不添加,则不需要提供 item 参数。
  • 如果删除的部分是稀疏的,则splice()返回的数组也是稀疏的,对应的索引为空槽。
30.2 手写实现
MyArray.prototype.splice = function(start, deleteCount, ...items) {
    let length = this.length >>> 0;
    // 处理索引
    let actualStart = start < 0 ? Math.max(length + start, 0) : Math.min(start, length);
    let actualDeleteCount;

    if (arguments.length === 1) {
        // 只有start的情况下,会删除从start开始到结尾的元素
        actualDeleteCount = length - actualStart;
    } else if (arguments.length >= 2) {
        // 确保删除数目的合法性,最多只能到数组结尾
        actualDeleteCount = Math.min(Math.max(Number(deleteCount), 0), length - actualStart);
    } else {
        actualDeleteCount = 0;
    }

    let removedElements = new MyArray();

    // 取出要删除的元素-保留空槽
    for (let i = 0; i < actualDeleteCount; i++) {
        let fromIndex = actualStart + i;
        if (fromIndex in this) {
            removedElements[i] = this[fromIndex];
        }
    }
    removedElements.length = actualDeleteCount;

    // 添加和删除元素的数量差
    let itemCount = items.length;
    let shiftCount = itemCount - actualDeleteCount;

    if (shiftCount > 0) {
        // 需要后移元素
        for (let i = length - 1; i >= actualStart + actualDeleteCount; i--) {
            let fromIndex = i;
            let toIndex = i + shiftCount;
            if (fromIndex in this) {
                this[toIndex] = this[fromIndex];
            } else {
                delete this[toIndex];
            }
        }
    } else if (shiftCount < 0) {
        // 需要前移元素
        for (let i = actualStart + actualDeleteCount; i < length; i++) {
            let fromIndex = i;
            let toIndex = i + shiftCount;
            if (fromIndex in this) {
                this[toIndex] = this[fromIndex];
            } else {
                delete this[toIndex];
            }
        }
        // 删除多余的元素
        for (let i = length - 1; i >= length + shiftCount; i--) {
            delete this[i];
        }
    }

    // 插入新元素
    for (let i = 0; i < itemCount; i++) {
        this[actualStart + i] = items[i];
    }

    // 更新 length
    this.length = length + shiftCount;

    return removedElements;
};

// 测试用例
let arr_9 = new MyArray(1, 2, 3, 4, 5);
let removed = arr_9.splice(2, 2, 'a', 'b');
console.log(arr_9); // [1, 2, 'a', 'b', 5]
console.log(removed); // [3, 4]

let arr_10 = new MyArray(1, 2, 3);
let removed2 = arr_10.splice(1);
console.log(arr_10); // [1]
console.log(removed2); // [2, 3]

let arr_11 = new MyArray(1, 2, 3);
let removed3 = arr_11.splice(-1, 0, 4, 5);
console.log(arr_11); // [1, 2, 4, 5, 3]
console.log(removed3); // []

难点总结

  • 计算索引及计算合法删除数量:需要正确处理 startdeleteCount,特别是负索引的情况。还要注意删除元素个数不能超过数组长度。
  • 元素移动与空槽处理:在添加或删除元素时,需要根据增加的元素和删除的元素正确移动数组中的其他元素,注意空槽的移动。
  • 更新 length 属性:操作完成后,需要更新数组的 length 属性。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Pikachu-暴力破解-验证码绕过(on client)

访问页面&#xff0c; 从burpsuite 上看到返回的源代码&#xff1b; 验证码生成时通过 createCode 方法生成&#xff0c;在前端页面生成&#xff1b; 同时也是在前端做的校验&#xff1b; 直接验证&#xff1b;F12 -- 网络&#xff0c;随便输入个账号、密码、验证码&#xff0…

C初阶(八)选择结构(分支结构)--if、else、switch

前言&#xff1a; C语言是用来解决问题的&#xff0c;除了必要的数据输入与输出&#xff08;见前文&#xff09;&#xff0c;还要有逻辑结构。其中基本可以归为三类&#xff1a;顺序结构、选择结构、循环结构。今天&#xff0c;杰哥提笔写的是关于选择结构&#xff08;又叫“分…

CSP-J Day 5 模拟赛补题报告

姓名&#xff1a;王胤皓&#xff0c;校区&#xff1a;和谐校区&#xff0c;考试时间&#xff1a; 2024 2024 2024 年 10 10 10 月 5 5 5 日 9 : 00 : 00 9:00:00 9:00:00~ 12 : 30 : 00 12:30:00 12:30:00&#xff0c;学号&#xff1a; S 07738 S07738 S07738 请关注作者的…

9.30学习记录(补)

手撕线程池: 1.进程:进程就是运行中的程序 2.线程的最大数量取决于CPU的核数 3.创建线程 thread t1; 在使用多线程时&#xff0c;由于线程是由上至下走的&#xff0c;所以主程序要等待线程全部执行完才能结束否则就会发生报错。通过thread.join()来实现 但是如果在一个比…

CentOS 替换 yum源 经验分享

视频教程在bilibili:CentOS 替换 yum源 经验分享_哔哩哔哩_bilibili问题原因 解决方法 1. 进入镜像目录 [rootlocalhost ~]# cd /etc/yum.repos.d/ 2.备份文件 [rootlocalhost yum.repos.d]# rename repo bak * 3.寻找阿里镜像源复制 https://developer.aliyun.com/mirror/ …

Redis基础三(redis的高级配置)

Redis进阶配置 一、Redis持久化操作 ​ 持久化就是把内存的数据写到磁盘中去&#xff0c;防止服务宕机了内存数据丢失。&#xff08;Redis 数据都放在内存中。如果机器挂掉&#xff0c;内存的数据就不存在。所以需要做持久化&#xff0c;将内存中的数据保存在磁盘&#xff0c…

聊聊Mysql的MVCC

1 什么是MVCC&#xff1f; MVCC&#xff0c;是Multiversion Concurrency Control的缩写&#xff0c;翻译过来是多版本并发控制&#xff0c;和数据库锁一样&#xff0c;他也是一种并发控制的解决方案。 我们知道&#xff0c;在数据库中&#xff0c;对数据的操作主要有2种&#…

分享9个论文写作中强化观点三要素的奇技淫巧

学境思源&#xff0c;一键生成论文初稿&#xff1a; AcademicIdeas - 学境思源AI论文写作 在学术写作中&#xff0c;强化观点的表达至关重要&#xff0c;它不仅能够提升论文的说服力&#xff0c;还能使论点更加明确和有力。为了帮助作者更有效地传达观点&#xff0c;本文将分享…

Leetcode 1631. 最小体力消耗路径

1.题目基本信息 1.1.题目描述 你准备参加一场远足活动。给你一个二维 rows x columns 的地图 heights &#xff0c;其中 heights[row][col] 表示格子 (row, col) 的高度。一开始你在最左上角的格子 (0, 0) &#xff0c;且你希望去最右下角的格子 (rows-1, columns-1) &#x…

【Godot4.3】复合路径类myPath

概述 之前编写过一个基于指令绘图的类交myPoint&#xff0c;但是只涉及折线段生成。这次我基于SVG的<path>标签路径指令的启发&#xff0c;实现了一个能够获得连续绘制的直线段、圆弧和贝塞尔复合路径的类型myPath。 可以使用绘图指令方法或字符串形式的绘图指令解析来…

MATLAB|基于多主体主从博弈的区域综合能源系统低碳经济优化调度

目录 主要内容 程序亮点&#xff1a; 模型研究 一、综合能源模型 二、主从博弈框架 部分代码 结果一览 下载链接 主要内容 程序参考文献《基于多主体主从博弈的区域综合能源系统低碳经济优化调度》&#xff0c;采用了区域综合能源系统多主体博弈协同优化方…

【重学 MySQL】五十二、MySQL8 新特性:计算列

【重学 MySQL】五十二、MySQL8 新特性&#xff1a;计算列 定义特性用法应用场景注意事项 在MySQL8中&#xff0c;计算列是一项引入的新特性&#xff0c;它为数据处理和分析提供了更大的灵活性和便捷性。 定义 计算列是指根据数据库中其他列的值通过计算得出的新列&#xff0c…

反调试—1

IsDebuggerPresent() CheckRemoteDebuggerPresent() 其内部实际调用NtQueryInformationProcess() bool _stdcall ThreadCall() {while (true){BOOL pbDebuggerPresent FALSE;CheckRemoteDebuggerPresent(GetCurrentProcess(), &pbDebuggerPresent);if (pbDebuggerPres…

Leetcode: 0011-0020题速览

Leetcode: 0011-0020题速览 本文材料来自于LeetCode solutions in any programming language | 多种编程语言实现 LeetCode、《剑指 Offer&#xff08;第 2 版&#xff09;》、《程序员面试金典&#xff08;第 6 版&#xff09;》题解 遵从开源协议为知识共享 版权归属-相同方式…

【持续更新中】MMDetection3训练自己的数据集常见报错解决

博主近来跑自己数据集需要对比试验&#xff0c;故选择了MMDetection3这一算法整合详细的框架&#xff0c;遇到了较多问题在此处留作记录&#xff0c;若你也有相应的问题可以在评论区提出与解决方法。会持续更新&#xff0c;同时欢迎批评指正。 0.ModuleNotFoundError: No modu…

微信小程序hbuilderx+uniapp+Android 新农村综合风貌旅游展示平台

目录 项目介绍支持以下技术栈&#xff1a;具体实现截图HBuilderXuniappmysql数据库与主流编程语言java类核心代码部分展示登录的业务流程的顺序是&#xff1a;数据库设计性能分析操作可行性技术可行性系统安全性数据完整性软件测试详细视频演示源码获取方式 项目介绍 小程序端…

索尼MDR-M1:超宽频的音频盛宴,打造沉浸式音乐体验

在音乐的世界里&#xff0c;每一次技术的突破都意味着全新的听觉体验。 索尼&#xff0c;作为音频技术的先锋&#xff0c;再次以其最新力作——MDR-M1封闭式监听耳机&#xff0c;引领了音乐界的新潮流。 这款耳机以其超宽频播放和卓越的隔音性能&#xff0c;为音乐爱好者和专…

多模态—图文匹配

可能最近大家已经发现了chatgpt可以根据自己的描述生成图片&#xff0c;其实这就是一个图文匹配的问题&#xff0c;可以理解为这是一个多模态的问题。 在模型训练时我们需要N个图片和N个文本对进行训练&#xff0c;文本通过text encoder形成文本语义向量&#xff0c;text enco…

【Python】Streamlit:为数据科学与机器学习打造的简易应用框架

Streamlit 是一个开源的 Python 库&#xff0c;专为数据科学家和机器学习开发者设计&#xff0c;旨在快速构建数据应用。通过简单的 Python 脚本&#xff0c;开发者无需掌握前端技术&#xff0c;即可将数据分析和模型结果转化为直观、交互式的 Web 应用。其简洁的 API 设计使得…

NVIDIA NVLink-C2C

NVIDIA NVLink-C2C 文章目录 前言一、介绍1. 用于定制芯片集成的超快芯片互连技术2. 构建半定制芯片设计3. 使用 NVLink-C2C 技术的产品 二、NVLink-C2C 技术优势1. 高带宽2. 低延迟3. 低功率和高密度4. 行业标准协议 前言 将 NVLink 扩展至芯片级集成 一、介绍 1. 用于定制芯…