一、按方法分类的数组过滤技术
方法一:使用 filter() + Set 进行高效过滤
基本语法
const smallSet = new Set(smallArray);
const result = bigArray.filter(item => !smallSet.has(item));
纯数组示例
const bigArray = [1, 2, 3, 4, 5, 6, 7, 8];
const smallArray = [2, 4, 6, 9];
const smallSet = new Set(smallArray);
const result = bigArray.filter(item => !smallSet.has(item));
console.log(result);
对象数组示例
const bigArray = [
{ id: 1, name: "Alice" },
{ id: 2, name: "Bob" },
{ id: 3, name: "Charlie" },
{ id: 4, name: "David" },
];
const smallArray = [
{ id: 2, name: "Bob" },
{ id: 4, name: "David" },
{ id: 5, name: "Eve" },
];
const smallIds = new Set(smallArray.map(item => item.id));
const result = bigArray.filter(item => !smallIds.has(item.id));
console.log(result);
方法二:使用 filter() + includes() 进行简单过滤
基本语法
const result = bigArray.filter(item => !smallArray.includes(item));
纯数组示例
const bigArray = [1, 2, 3, 4, 5];
const smallArray = [2, 4];
const result = bigArray.filter(item => !smallArray.includes(item));
console.log(result);
对象数组示例
const bigArray = [
{ id: 1, name: "Alice" },
{ id: 2, name: "Bob" },
{ id: 3, name: "Charlie" },
];
const smallArray = [
{ id: 2, name: "Bob" },
{ id: 4, name: "David" },
];
const smallIds = smallArray.map(item => item.id);
const result = bigArray.filter(item => !smallIds.includes(item.id));
console.log(result);
方法三:使用 filter() + find() 或 filter() + some() 进行复杂条件过滤
基本语法
const result = bigArray.filter(item => !smallArray.find(smallItem =>
));
const result = bigArray.filter(item => !smallArray.some(smallItem =>
));
纯数组示例
const bigArray = [10, 20, 30, 40, 50];
const smallArray = [20, 40];
const result = bigArray.filter(item => !smallArray.some(smallItem => smallItem === item));
console.log(result);
对象数组示例
const bigArray = [
{ id: 1, name: "Alice", age: 25 },
{ id: 2, name: "Bob", age: 30 },
{ id: 3, name: "Charlie", age: 25 },
];
const smallArray = [
{ id: 2, name: "Bob" },
{ name: "David" },
];
const result = bigArray.filter(
bigItem =>
!smallArray.some(
smallItem =>
(smallItem.id && smallItem.id === bigItem.id) ||
(smallItem.name && smallItem.name === bigItem.name)
)
);
console.log(result);
方法四:使用 filter() + Map 进行高性能对象过滤
基本语法
const smallMap = new Map(smallArray.map(item => [key, item]));
const result = bigArray.filter(item => !smallMap.has(item[key]));
对象数组示例
const bigArray = [
{ id: 1, name: "Alice" },
{ id: 2, name: "Bob" },
{ id: 3, name: "Charlie" },
{ id: 4, name: "David" },
];
const smallArray = [
{ id: 2, name: "Bob" },
{ id: 4, name: "David" },
];
const smallMap = new Map(smallArray.map(item => [item.id, item]));
const result = bigArray.filter(item => !smallMap.has(item.id));
console.log(result);
方法五:使用 reduce() 进行累积过滤
基本语法
const result = bigArray.reduce((acc, item) => {
if (!shouldFilter(item)) {
acc.push(item);
}
return acc;
}, []);
纯数组示例
const bigArray = [1, 2, 3, 4, 5];
const smallArray = [2, 4];
const result = bigArray.reduce((acc, item) => {
if (!smallArray.includes(item)) {
acc.push(item);
}
return acc;
}, []);
console.log(result);
对象数组示例
const bigArray = [
{ id: 1, name: "Alice" },
{ id: 2, name: "Bob" },
{ id: 3, name: "Charlie" },
];
const smallArray = [{ id: 2, name: "Bob" }];
const smallIds = new Set(smallArray.map(item => item.id));
const result = bigArray.reduce((acc, item) => {
if (!smallIds.has(item.id)) {
acc.push(item);
}
return acc;
}, []);
console.log(result);
方法六:使用 for 循环(性能最优)
基本语法
function arrayDifference(bigArray, smallArray, key = null) {
const result = [];
const smallSet = createFilterSet(smallArray, key);
for (let i = 0; i < bigArray.length; i++) {
const item = bigArray[i];
const compareValue = key ? item[key] : item;
if (!smallSet.has(compareValue)) {
result.push(item);
}
}
return result;
}
function createFilterSet(array, key) {
if (!array.length) return new Set();
if (key) {
const values = [];
for (let i = 0; i < array.length; i++) {
if (array[i] && array[i][key] !== undefined) {
values.push(array[i][key]);
}
}
return new Set(values);
} else {
return new Set(array);
}
}
纯数组示例
function filterPureArrays(bigArray, smallArray) {
const result = [];
const smallSet = new Set(smallArray);
for (let i = 0; i < bigArray.length; i++) {
if (!smallSet.has(bigArray[i])) {
result.push(bigArray[i]);
}
}
return result;
}
const bigArray = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
const smallArray = [2, 4, 6, 8, 12];
console.log(filterPureArrays(bigArray, smallArray));
对象数组示例
function filterObjectArrays(bigArray, smallArray, key = "id") {
const result = [];
const idSet = new Set();
for (let i = 0; i < smallArray.length; i++) {
if (smallArray[i] && smallArray[i][key] !== undefined) {
idSet.add(smallArray[i][key]);
}
}
for (let i = 0; i < bigArray.length; i++) {
const item = bigArray[i];
if (item && item[key] !== undefined && !idSet.has(item[key])) {
result.push(item);
}
}
return result;
}
const bigArray = [
{ id: 1, name: "Alice", age: 25 },
{ id: 2, name: "Bob", age: 30 },
{ id: 3, name: "Charlie", age: 35 },
{ id: 4, name: "David", age: 28 },
{ id: 5, name: "Eve", age: 32 },
];
const smallArray = [
{ id: 2, name: "Bob" },
{ id: 4, name: "David" },
{ id: 6, name: "Frank" },
];
console.log(filterObjectArrays(bigArray, smallArray, "id"));
二、Set.has() vs Array.includes() 深度对比
1.性能测试代码
function performanceTest() {
const sizes = [100, 1000, 10000, 100000];
sizes.forEach(size => {
console.log(`\n=== 数据量: ${size} 个元素 ===`);
const bigArray = Array.from({ length: size }, (_, i) => i);
const smallArray = bigArray.filter((_, i) => i % 3 === 0);
console.time("Set方法");
const smallSet = new Set(smallArray);
const result1 = bigArray.filter(item => !smallSet.has(item));
console.timeEnd("Set方法");
console.time("includes方法");
const result2 = bigArray.filter(item => !smallArray.includes(item));
console.timeEnd("includes方法");
console.log("结果一致:", JSON.stringify(result1) === JSON.stringify(result2));
});
}
performanceTest();
2.性能差异分析表
| 数据量 |
Set.has() 时间 |
Array.includes() 时间 |
性能差异 |
| 100 元素 |
~0.1ms |
~0.3ms |
3 倍 |
| 1000 元素 |
~0.2ms |
~8ms |
40 倍 |
| 10000 元素 |
~0.5ms |
~250ms |
500 倍 |
3.时间复杂度对比
| 方法 |
平均时间复杂度 |
最坏情况 |
空间复杂度 |
Array.includes() |
O(n) |
O(n) |
O(1) |
Set.has() |
O(1) |
O(n) |
O(n) |
4.底层实现原理
Array.includes() 实现原理
Array.prototype.includes = function (searchElement) {
for (let i = 0; i < this.length; i++) {
if (this[i] === searchElement) {
return true;
}
}
return false;
};
- 线性搜索算法
- 需要遍历整个数组
- 性能与数组长度成正比
Set.has() 实现原理
class SimpleSet {
constructor() {
this.buckets = [];
this.bucketCount = 16;
}
add(value) {
const bucketIndex = this.getBucketIndex(value);
if (!this.buckets[bucketIndex]) {
this.buckets[bucketIndex] = [];
}
this.buckets[bucketIndex].push(value);
}
has(value) {
const bucketIndex = this.getBucketIndex(value);
const bucket = this.buckets[bucketIndex];
if (!bucket) return false;
return bucket.some(item => item === value);
}
getBucketIndex(value) {
const hash = this.hashFunction(value);
return hash % this.bucketCount;
}
hashFunction(value) {
return JSON.stringify(value).length;
}
}
- 哈希表数据结构
- 通过哈希函数直接定位
- 平均情况下常数时间访问,查找时间与数据量无关
5.最佳实践选择指南
选择 Set.has() 的情况:
- 数据量较大(> 50 个元素)
- 需要频繁过滤操作
- 性能敏感的应用场景
- 多次使用同一个过滤条件
const largeDataset = Array.from({ length: 100000 }, (_, i) => i);
const searchSet = new Set(largeDataset);
for (let i = 0; i < 1000; i++) {
searchSet.has(Math.random() * 100000);
}
const duplicateArray = [1, 2, 2, 3, 4, 4, 5];
const uniqueArray = [...new Set(duplicateArray)];
const setA = new Set([1, 2, 3]);
const setB = new Set([2, 3, 4]);
const union = new Set([...setA, ...setB]);
const intersection = new Set([...setA].filter(x => setB.has(x)));
const difference = new Set([...setA].filter(x => !setB.has(x)));
选择 Array.includes() 的情况:
- 数据量很小(< 20 个元素)
- 单次或极少次操作
- 代码简洁性优先
- 过滤条件简单且不重复使用
const smallArray = ["apple", "banana", "orange"];
if (smallArray.includes("apple")) {
console.log("找到苹果!");
}
const config = ["debug", "test", "production"];
const currentEnv = "production";
if (config.includes(currentEnv)) {
}
const validStatuses = ["pending", "processing", "completed"];
function isValidStatus(status) {
return validStatuses.includes(status);
}
选择 find()/some() 的情况:
- 需要复杂匹配条件
- 对象的多属性匹配
- 动态过滤条件
const users = [
{ id: 1, role: "admin" },
{ id: 2, role: "user" },
];
const forbiddenConditions = [{ role: "admin" }, { id: 2 }];
const allowedUsers = users.filter(
user =>
!forbiddenConditions.some(condition =>
Object.keys(condition).every(key => user[key] === condition[key])
)
);
三、通用过滤函数封装
function filterArray(mainArray, filterArray, keyOrFn = null, options = {}) {
const { useSet = true } = options;
if (!mainArray.length) return [];
if (!filterArray.length) return [...mainArray];
if (keyOrFn === null || typeof mainArray[0] !== "object") {
return useSet && filterArray.length > 20
? filterWithSet(mainArray, filterArray)
: filterWithIncludes(mainArray, filterArray);
}
if (typeof keyOrFn === "string") {
const key = keyOrFn;
return useSet && filterArray.length > 10
? filterObjectsWithSet(mainArray, filterArray, key)
: filterObjectsWithIncludes(mainArray, filterArray, key);
}
if (typeof keyOrFn === "function") {
return mainArray.filter(item => !filterArray.some(filterItem => keyOrFn(item, filterItem)));
}
return mainArray;
}
function filterWithSet(mainArray, filterArray) {
const filterSet = new Set(filterArray);
return mainArray.filter(item => !filterSet.has(item));
}
function filterWithIncludes(mainArray, filterArray) {
return mainArray.filter(item => !filterArray.includes(item));
}
function filterObjectsWithSet(mainArray, filterArray, key) {
const filterSet = new Set(filterArray.map(item => item[key]));
return mainArray.filter(item => !filterSet.has(item[key]));
}
function filterObjectsWithIncludes(mainArray, filterArray, key) {
const filterValues = filterArray.map(item => item[key]);
return mainArray.filter(item => !filterValues.includes(item[key]));
}
const numbers = [1, 2, 3, 4, 5];
const toRemove = [2, 4];
console.log(filterArray(numbers, toRemove));
const users = [
{ id: 1, name: "A" },
{ id: 2, name: "B" },
{ id: 3, name: "C" },
];
const toRemoveUsers = [{ id: 2 }, { id: 3 }];
console.log(filterArray(users, toRemoveUsers, "id"));