当前位置: 首页 > article >正文

js 中 如何获取数组的交集【面试题】

一、数组元素为基本类型:Number、String、等基本类型时

1、使用 Set 和 filter(适用于两个数组)
const intersection = (arr1, arr2) => {
  const set = new Set(arr2);
  return [...new Set(arr1)].filter(item => set.has(item));
};

将第二个数组转为 Set 提高查询效率,然后遍历第一个数组并过滤出同时存在于 Set 中的元素。使用 [...new Set(arr1)] 确保第一个数组中的元素唯一。

2、处理多个数组的交集
const intersection = (...arrays) => {
  if (arrays.length === 0) return [];
  // 以第一个数组为基准,转为 Set 去重
  let result = new Set(arrays[0]);
  for (const arr of arrays.slice(1)) {
    const currentSet = new Set(arr);
    // 过滤出同时存在于当前数组的元素
    result = new Set([...result].filter(item => currentSet.has(item)));
    if (result.size === 0) break; // 提前终止无意义遍历
  }
  return [...result];
};

依次用每个数组的 Set 过滤基准集合,最终返回所有数组的共同元素。

const arr1 = [1, 2, 2, 3];
const arr2 = [2, 3, 4];
const arr3 = [3, 4, 5];

console.log(intersection(arr1, arr2));       // 输出: [2, 3]
console.log(intersection(arr1, arr2, arr3)); // 输出: [3]

时间复杂度优化:使用 Set 将查找操作降至 (1)O(1),整体效率较高。

元素唯一性:返回的交集元素始终唯一。

空数组处理:如果输入空数组或交集为空,直接返回空数组。

3、注意事项

严格相等:Set 使用 === 判断元素是否相同,对象元素需额外处理。
兼容性:Set 和 filter 在现代浏览器中支持良好,若需支持旧环境(如 IE),需引入 Polyfill。

二、数组元素为引用类型时:引用类型需要对比值,而不是内存中的地址

1、基于唯一标识符(推荐)

如果对象有唯一标识符(如 id),可以通过该属性快速判断是否属于交集:

const getObjectIntersection = (key, ...arrays) => {
  if (arrays.length === 0) return [];
  // 统计每个对象标识符出现的次数
  const countMap = new Map();
  arrays.forEach(arr => {
    const uniqueItems = Array.from(new Set(arr.map(item => item[key])));
    uniqueItems.forEach(id => {
      countMap.set(id, (countMap.get(id) || 0) + 1);
    });
  });
  // 筛选出现次数等于数组数量的标识符
  const resultIds = Array.from(countMap.entries())
    .filter(([_, count]) => count === arrays.length)
    .map(([id]) => id);
  // 返回第一个数组中符合条件的所有对象(保留原始数据)
  return arrays[0].filter(item => resultIds.includes(item[key]));
};

// 示例
const arr1 = [{ id: 1, name: 'A' }, { id: 2, name: 'B' }];
const arr2 = [{ id: 2, name: 'B' }, { id: 3, name: 'C' }];
const arr3 = [{ id: 2, name: 'B' }, { id: 4, name: 'D' }];

console.log(getObjectIntersection('id', arr1, arr2, arr3));
// 输出: [{ id: 2, name: 'B' }]

2、通用对象比较(深比较)

如果对象没有唯一标识符,或需要比较所有属性,可以使用 深比较 方法:

const deepEqual = (obj1, obj2) => {
  if (obj1 === obj2) return true;
  if (typeof obj1 !== 'object' || typeof obj2 !== 'object' || !obj1 || !obj2) return false;
  const keys1 = Object.keys(obj1), keys2 = Object.keys(obj2);
  if (keys1.length !== keys2.length) return false;
  return keys1.every(key => keys2.includes(key) && deepEqual(obj1[key], obj2[key]));
};

const getObjectIntersection = (...arrays) => {
  return arrays.reduce((acc, currentArr) => {
    return acc.filter(accItem => 
      currentArr.some(currentItem => deepEqual(accItem, currentItem))
    );
  });
};

// 示例
const obj1 = { id: 1, data: { value: 'X' } };
const obj2 = { id: 1, data: { value: 'X' } }; // 内容相同但引用不同
const arr1 = [obj1, { id: 2 }];
const arr2 = [obj2, { id: 2 }];

console.log(getObjectIntersection(arr1, arr2));
// 输出: [{ id: 2 }](obj1 和 obj2 内容相同但引用不同,未被识别为相同)
3、序列化比较(JSON.stringify)

如果对象结构简单且无循环引用,可通过序列化简化比较:

const getObjectIntersection = (...arrays) => {
  if (arrays.length === 0) return [];
  // 序列化所有对象
  const serializedArrays = arrays.map(arr => [
    ...new Set(arr.map(item => JSON.stringify(item)))
  ]);
  // 统计序列化后的字符串出现次数
  const countMap = new Map();
  serializedArrays.forEach(arr => {
    arr.forEach(str => {
      countMap.set(str, (countMap.get(str) || 0) + 1);
    });
  });
  // 筛选出现次数等于数组数量的字符串
  const commonStrings = Array.from(countMap.entries())
    .filter(([_, count]) => count === arrays.length)
    .map(([str]) => str);
  // 反序列化并返回第一个数组中匹配的对象
  return arrays[0].filter(item => 
    commonStrings.includes(JSON.stringify(item))
  );
};
4、注意事项

性能问题深比较和序列化在大数据量时性能较差,优先使用唯一标识符方案。
对象引用:如果多个数组中的对象实际上是同一个引用,直接使用 Set 即可。
嵌套对象:深比较需要递归处理嵌套对象,确保所有层级属性一致。
特殊类型JSON 序列化会丢失 undefined、函数和循环引用,需谨慎使用。


http://www.kler.cn/a/600093.html

相关文章:

  • 如何为AI开发选择合适的服务器?
  • 《HarmonyOS Next群头像拼接与组件截图技术解析》
  • 第六届IEEE人工智能、网络与信息技术国际学术会议(AINIT 2025)
  • “我是GM”之NAS搭建Luanti游戏服务器,开启沙盒游戏新体验
  • WPS中把多张PPT打印到一张A4纸上
  • Jenkins 共享库(Shared Libraries)使用说明文档
  • WordPress子主题插件 Child Theme Configurator
  • HarmonyOS Next~鸿蒙AI功能开发:Core Speech Kit与Core Vision Kit的技术解析与实践
  • 论文阅读笔记——MTGS: Multi-Traversal Gaussian Splatting
  • Gitee上库常用git命令
  • 微信小程序中使用Less样式方法
  • Flask的app.run()里发生了什么
  • 软件测试面试:支付功能如何测试?
  • WordPress 晨风自定义插件
  • 玩客云 armbian 安装mqtt服务端
  • Python中的类
  • ES如果要查10条数据需要从各个分片上各取多少条数据?
  • 如何实现一个纯 CSS 的滑动门导航效果,需要用到哪些技术?
  • 【Java全栈进阶架构师实战:从设计模式到SpringCloudAlibaba,打造高可用系统】
  • ChatGPT降低论文AIGC重复率的提示词合集(高效降重方法)