侧边栏壁纸
  • 累计撰写 241 篇文章
  • 累计创建 292 个标签
  • 累计收到 0 条评论

目 录CONTENT

文章目录

JavaScript数组专题之6:数组的实用技巧与案例集锦

DGF
DGF
2025-10-29 / 0 评论 / 0 点赞 / 11 阅读 / 0 字

JS 数组系列专题文章目录

入门篇

第 1 篇:认识 JavaScript 数组——从入门到操作基础
👉 介绍数组的概念、创建方式、索引与长度的使用,以及最基础的遍历方法。适合刚接触数组的新手。

第 2 篇:数组的增删改全解析(push、pop、splice)
👉 系统讲解数组的增删改方法,包括 pushpopshiftunshiftsplicefill,并结合小案例说明。

常用方法篇

第 3 篇:数组的查找与判断方法大全
👉 详细介绍如何在数组中查找元素,包括 indexOflastIndexOfincludesfindfindIndexsomeevery 等。

第 4 篇:数组的排序与合并技巧(sort、concat、slice)
👉 讲解数组排序与反转的方法,以及合并、切片、字符串化的常见操作,重点讲解 sort 排序中的注意点。

进阶篇

第 5 篇:数组的迭代与转换(map、filter、reduce 全解)
👉 深入剖析高阶方法 mapfilterreduce,包括实际应用场景:数组求和、数据统计、扁平化处理等。

第 6 篇:数组的实用技巧与案例集锦
👉 介绍常见的数组处理技巧:去重(多种写法对比)、扁平化(flat 与递归实现)、字符串与数组互转、对象数组排序。

高阶篇

第 7 篇:类数组与稀疏数组——容易忽略的细节
👉 解释什么是类数组(argumentsNodeList),如何转换为真数组,稀疏数组的特性和坑点。

第 8 篇:不可变数组操作与函数式编程思路
👉 如何在不修改原数组的前提下进行操作(扩展运算符、concatmap 等),结合函数式编程的思想讲解。

实战 & 面试篇

第 9 篇:手写数组常用方法(map、filter、reduce)
👉 从源码角度模拟实现常见数组方法,帮助加深理解,提升面试竞争力。

第 10 篇:数组实战应用与高频面试题精选
👉 总结数组在实战中的应用:两数之和、数组旋转、最大最小值查找、groupBy、分页实现,涵盖面试常考题。

1. 去重(去除重复元素)

1.1 基本类型(数字 / 字符串 / 布尔)

最简单、最快的办法是 Set

const arr = [1, 2, 2, 3, 1];
const unique = [...new Set(arr)];
console.log(unique); // [1, 2, 3]
  • 原理Set 只保留首次出现的值,保留插入顺序。
  • 复杂度:O(n)。适合大多数场景。

1.2 兼容 NaN 情况

Set 能正确处理 NaN(把多个 NaN 视为同一个),而 indexOf 不行:

const a = [NaN, NaN];
console.log([...new Set(a)]); // [NaN]

1.3 对象数组(按某个 key 去重)

对象不能直接用 Set 去重(比较引用)。常用方法:用 Map 或用一个键(id、name)做标识:

function uniqueByKey(arr, key) {
  const seen = new Map();
  return arr.filter(item => {
    const k = item[key];
    if (seen.has(k)) return false;
    seen.set(k, true);
    return true;
  });
}

// 示例
const users = [
  { id: 1, name: 'A' },
  { id: 2, name: 'B' },
  { id: 1, name: 'A2' } // id 重复
];
console.log(uniqueByKey(users, 'id'));
// [{id:1,name:'A'}, {id:2,name:'B'}]
  • 适合场景:数据有稳定唯一字段(如 idemail)时最靠谱。

1.4 对象按内容去重(无唯一 key)

可用 JSON.stringify 作为简易方案(注意属性顺序会影响结果):

function uniqueByJSON(arr) {
  const seen = new Set();
  return arr.filter(item => {
    const key = JSON.stringify(item);
    if (seen.has(key)) return false;
    seen.add(key);
    return true;
  });
}
  • 注意:不适合含有函数、undefinedDate 等复杂类型,且对象属性顺序不同会被认为不同。生产环境建议用规范化函数或指定 key。

2. 扁平化(Flatten)

2.1 用内置 flat

const nested = [1, [2, [3, [4]]]];
console.log(nested.flat(1)); // [1, 2, [3, [4]]]
console.log(nested.flat(2)); // [1, 2, 3, [4]]
console.log(nested.flat(Infinity)); // [1,2,3,4]
  • depth 参数默认 1Infinity 表示完全扁平化。

2.2 递归实现(自定义深度)

function flatten(arr, depth = 1) {
  const res = [];
  for (const item of arr) {
    if (Array.isArray(item) && depth > 0) {
      res.push(...flatten(item, depth - 1));
    } else {
      res.push(item);
    }
  }
  return res;
}

// 用法
flatten([1, [2, [3]]], 2); // [1,2,3]
  • 优点:兼容旧环境或做特殊处理(比如在展开时做过滤、转换等)。

3. 数组 ⇄ 字符串(split / join)

3.1 join 把数组变成字符串

const arr = ['apple', 'banana', 'orange'];
console.log(arr.join(', ')); // "apple, banana, orange"
console.log([].join('-')); // ""(空数组变空字符串)
  • join(separator) 默认 separator,

3.2 split 把字符串变成数组

const s = "a,b,c";
console.log(s.split(',')); // ["a","b","c"]
console.log("".split(',')); // [""] 注意:空字符串返回 [""],不是 []

3.3 简易 CSV 注意点

split(',') 对简单 CSV OK,但不能应对带引号、带逗号的字段。复杂 CSV 请用专门解析库(如 PapaParse)。(提示:这也是常见坑)

4. 对象数组排序技巧(多字段 & locale 比较)

4.1 按数字字段排序

students.sort((a, b) => a.score - b.score); // 升序

4.2 多字段排序(先按 age,再按 name)

students.sort((a, b) => {
  const diff = a.age - b.age;
  if (diff !== 0) return diff; // 年龄不同,按年龄
  return a.name.localeCompare(b.name); // 年龄相同,按名字
});
  • localeCompare 可处理中文/大小写/本地化排序,支持 localeCompare(other, locale, options) 的高级用法。

5. 分页 / 切块(chunk)

5.1 实现 chunk(arr, size)

function chunk(arr, size) {
  if (size <= 0) throw new Error('size must be > 0');
  const res = [];
  for (let i = 0; i < arr.length; i += size) {
    res.push(arr.slice(i, i + size));
  }
  return res;
}

// 示例
chunk([1,2,3,4,5], 2); // [[1,2], [3,4], [5]]
  • 使用场景:分页显示、分批上传、批处理。

5.2 根据页码获取该页内容

function getPage(arr, page = 1, pageSize = 10) {
  const total = Math.ceil(arr.length / pageSize);
  const p = Math.max(1, Math.min(page, total));
  const start = (p - 1) * pageSize;
  return {
    page: p,
    pageSize,
    total,
    data: arr.slice(start, start + pageSize),
  };
}

6. 集合运算:并 / 交 / 差(针对原始值)

const a = [1,2,3];
const b = [2,3,4];

// 并集
const union = [...new Set([...a, ...b])]; // [1,2,3,4]

// 交集
const setB = new Set(b);
const intersection = a.filter(x => setB.has(x)); // [2,3]

// 差(a - b)
const difference = a.filter(x => !setB.has(x)); // [1]
  • 复杂度:利用 Set,交/差 O(n)。

对于对象数组可用 key 提取或自定义比较。

7. 随机打乱(Fisher–Yates 洗牌)

正确且均匀的做法是 Fisher–Yates:

function shuffle(array) {
  const arr = array.slice(); // 复制,不破坏原数组
  for (let i = arr.length - 1; i > 0; i--) {
    const j = Math.floor(Math.random() * (i + 1)); // [0, i]
    [arr[i], arr[j]] = [arr[j], arr[i]];
  }
  return arr;
}
  • 注意:不要用 sort(() => Math.random() - 0.5) ——错误且不均匀。

8. 浅拷贝 / 深拷贝

8.1 浅拷贝(只复制第一层)

const a = [1,2,3];
const b = [...a]; // 或 a.slice()
  • 适合元素是原始类型的数组。若元素是对象,仍是引用。

8.2 深拷贝(常见方法)

  • 快速但有局限:JSON.parse(JSON.stringify(obj))(丢失 undefined、函数、Date→字符串、RegExp 等)
  • 标准且功能强:structuredClone(obj)(现代浏览器 / Node 支持)
const deep = structuredClone(obj);
  • 如果环境不支持 structuredClone,可以用 lodash.cloneDeep

9. 性能

  • 避免在循环里反复做 O(n) 操作:例如在循环里 .includes(),改用先把目标转为 Set
  • 减少中间数组:链式 filter().map().reduce() 虽语义清晰,但会创建中间数组;在超大数组或性能敏感场景,可用一次 reduce 完成多步。
  • 原地操作sort() / reverse() 会修改原数组,若要保留原件先 slice()[...arr] 复制一份。
  • 使用合适的数据结构:当需要频繁查找时,把数组转换成 Map/Set 能显著提高性能。

10. 综合实战:去重 + 排序 + 分页(完整示例)

把一组用户 ID(可能重复)处理成“去重、按 id 升序、取第 2 页(每页 3 条)”:

function process(ids, page = 2, pageSize = 3) {
  // 去重并排序
  const uniqueSorted = [...new Set(ids)].sort((a, b) => a - b);
  // 分页
  const start = (page - 1) * pageSize;
  return uniqueSorted.slice(start, start + pageSize);
}

const ids = [5,2,3,2,8,1,5,10,7];
console.log(process(ids, 2, 3)); // 如果 uniqueSorted = [1,2,3,5,7,8,10], 页2返回 [5,7,8]
0

评论区