Set 和 Map常用场景代码片段

一、Set 实用代码片段

1. 数组去重(基础版)

场景:接口返回数组、用户输入列表等需要快速去重。

javascript

运行

javascript 复制代码
/**
 * 数组去重(支持基本类型,引用类型需额外处理)
 * @param {Array} arr - 待去重数组
 * @returns {Array} 去重后数组
 */
const uniqueArray = (arr) => [...new Set(arr)];

// 示例
const arr = [1, 2, 2, 3, 'a', 'a'];
console.log(uniqueArray(arr)); // [1, 2, 3, 'a']

2. 检查数组是否有重复元素

场景:表单验证(如 "标签不可重复")、数据校验。

javascript

运行

javascript 复制代码
/**
 * 检查数组是否存在重复元素
 * @param {Array} arr - 待检查数组
 * @returns {boolean} 是否有重复
 */
const hasDuplicates = (arr) => new Set(arr).size !== arr.length;

// 示例
console.log(hasDuplicates([1, 2, 3])); // false
console.log(hasDuplicates([1, 2, 2])); // true

3. 集合操作(交集 / 并集 / 差集)

场景:权限对比(如 "用户权限与角色权限的交集")、数据筛选。

javascript

运行

ini 复制代码
// 交集:两个数组的共同元素
const intersection = (arr1, arr2) => {
  const set2 = new Set(arr2);
  return arr1.filter(item => set2.has(item));
};

// 并集:两个数组的所有元素(去重)
const union = (arr1, arr2) => [...new Set([...arr1, ...arr2])];

// 差集:arr1 有但 arr2 没有的元素
const difference = (arr1, arr2) => {
  const set2 = new Set(arr2);
  return arr1.filter(item => !set2.has(item));
};

// 示例
const a = [1, 2, 3];
const b = [2, 3, 4];
console.log(intersection(a, b)); // [2, 3]
console.log(union(a, b)); // [1, 2, 3, 4]
console.log(difference(a, b)); // [1]

4. 临时存储 "已处理项"(避免重复操作)

场景:批量处理数据时记录已处理 ID,防止重复请求 / 计算。

javascript

运行

scss 复制代码
// 记录已处理的任务ID
const processedTaskIds = new Set();

/**
 * 处理任务(仅处理未处理过的)
 * @param {number} taskId - 任务ID
 */
const processTask = (taskId) => {
  if (processedTaskIds.has(taskId)) {
    console.log(`任务 ${taskId} 已处理,跳过`);
    return;
  }
  // 模拟处理逻辑
  console.log(`处理任务 ${taskId}`);
  processedTaskIds.add(taskId);
};

// 示例
processTask(1); // 处理任务 1
processTask(1); // 任务 1 已处理,跳过
processTask(2); // 处理任务 2

二、Map 实用代码片段

1. 复杂键名映射(替代对象的局限性)

场景:用对象(如 DOM 元素、实例)作为键存储数据(对象的键会被转为字符串,无法直接用对象当键)。

javascript

运行

dart 复制代码
// 场景:给DOM元素绑定额外数据(如点击次数、状态)
const elementData = new Map();

// 获取DOM元素
const btn = document.getElementById('submit-btn');
const input = document.getElementById('username-input');

// 存储数据(键为DOM元素,值为任意类型)
elementData.set(btn, { clickCount: 0, disabled: false });
elementData.set(input, { value: '', touched: false });

// 更新数据
btn.addEventListener('click', () => {
  const data = elementData.get(btn);
  elementData.set(btn, { ...data, clickCount: data.clickCount + 1 });
  console.log(`按钮点击次数:${elementData.get(btn).clickCount}`);
});

2. 接口数据缓存(避免重复请求)

场景:同一参数的接口请求,优先返回缓存数据,减少接口调用。

javascript

运行

javascript 复制代码
/**
 * 带缓存的接口请求工具
 * @param {Function} fetchFn - 实际请求函数(返回Promise)
 * @returns {Function} 包装后的请求函数
 */
const withCache = (fetchFn) => {
  const cache = new Map(); // 缓存:键为参数字符串,值为请求结果

  return async (...args) => {
    // 生成唯一缓存键(将参数转为字符串,支持多参数)
    const cacheKey = JSON.stringify(args);

    // 命中缓存:直接返回
    if (cache.has(cacheKey)) {
      console.log('使用缓存数据');
      return cache.get(cacheKey);
    }

    // 未命中:请求并缓存
    console.log('发起新请求');
    const result = await fetchFn(...args);
    cache.set(cacheKey, result);
    return result;
  };
};

// 示例:包装一个获取用户信息的接口
const fetchUser = async (userId) => {
  const res = await fetch(`/api/user/${userId}`);
  return res.json();
};

// 使用缓存版请求
const fetchUserWithCache = withCache(fetchUser);

// 第一次请求(无缓存)
fetchUserWithCache(1); 
// 第二次请求同一用户(用缓存)
fetchUserWithCache(1); 

3. 多维度数据映射(快速查询)

场景:同一份数据需要通过多个 "键" 查询(如用户信息可通过 ID、手机号、用户名查询)。

javascript

运行

dart 复制代码
// 原始用户数据
const users = [
  { id: 1, name: '张三', phone: '13800138000' },
  { id: 2, name: '李四', phone: '13900139000' }
];

// 构建多维度映射
const userMaps = {
  byId: new Map(),    // 键:id
  byName: new Map(),  // 键:name
  byPhone: new Map()  // 键:phone
};

users.forEach(user => {
  userMaps.byId.set(user.id, user);
  userMaps.byName.set(user.name, user);
  userMaps.byPhone.set(user.phone, user);
});

// 快速查询示例
console.log(userMaps.byId.get(1)); // {id:1, name:'张三', ...}
console.log(userMaps.byPhone.get('13900139000')); // {id:2, ...}

4. 有序键值对遍历(保留插入顺序)

场景:需要按 "插入顺序" 遍历键值对(对象的键遍历顺序不稳定,尤其是数字键)。

javascript

运行

javascript 复制代码
// 场景:按用户操作顺序记录日志(需保留顺序)
const actionLog = new Map();

// 按顺序插入操作
actionLog.set('login', { time: '09:00', user: '张三' });
actionLog.set('view', { time: '09:05', page: '首页' });
actionLog.set('logout', { time: '10:00', user: '张三' });

// 按插入顺序遍历(Map 会保留插入顺序)
for (const [action, detail] of actionLog) {
  console.log(`${action}:${JSON.stringify(detail)}`);
}
// 输出顺序:login → view → logout(与插入顺序一致)

三、使用小贴士

  1. 选择 Set 还是 Map

    • 只需要 "唯一元素集合" → 用 Set;
    • 需要 "键值对映射"(尤其是复杂键) → 用 Map。
  2. 性能考量

    • Set/Map 的 has/get/set 操作时间复杂度为 O (1),比数组的 indexOf、includes等(O (n))更高效,数据量大时优先使用。
  3. 转换技巧

    • Set 转数组:[...mySet]Array.from(mySet)
    • Map 转对象(键为字符串时):Object.fromEntries(myMap)
  4. 转换技巧

    • Set/Map 的 has/get/set 操作都是 O (1),比数组 indexOf(O (n))、对象循环查询快,数据量大(>100)时优先用
    • 临时缓存(比如接口缓存)如果不需要持久化,用 Map 即可;需要持久化到 localStorage,要先转成数组 / 对象(因为 localStorage 只能存字符串)。
  5. 避坑提醒

    • Set 存引用类型(对象、数组)时,不会自动去重(因为引用地址不同),比如 new Set([{a:1}, {a:1}]) 会存两个对象。
    • Map 的键是 "引用相等",比如 map.set({}, 1)map.set({}, 2) 是两个不同的键(对象引用不同)。
相关推荐
Hilaku2 小时前
我为什么说全栈正在杀死前端?
前端·javascript·后端
程序猿_极客3 小时前
【期末网页设计作业】HTML+CSS+JS 旅行社网站、旅游主题设计与实现(附源码)
javascript·css·html·课程设计·期末网页设计
用户283209679373 小时前
为什么我的页面布局总是乱糟糟?可能是浮动和BFC在作怪!
javascript
会篮球的程序猿4 小时前
原生表格文本过长展示问题,参考layui长文本,点击出现文本域
前端·javascript·layui
哆啦A梦15884 小时前
48 我的地址页面布局
javascript·vue.js·node.js
bug爱好者4 小时前
vue3.x 使用vue3-tree-org实现组织架构图 + 自定义模版内容 - 附完整示例
前端·javascript·vue.js
flashlight_hi4 小时前
LeetCode 分类刷题:1669. 合并两个链表
javascript·leetcode·链表
码途进化论7 小时前
从Chrome跳转到IE浏览器的完整解决方案
前端·javascript
笙年7 小时前
Vue 基础配置新手总结
前端·javascript·vue.js