JavaScript数组过滤方法详解
在日常的编程工作中,我们经常需要从一个数组中筛选出符合条件的元素。JavaScript提供了多种方式来实现数组元素的过滤,其中Array.prototype.filter() 是最常用且最直观的方法。本文将系统性地介绍如何使用JavaScript过滤数组元素,涵盖从基础用法到实际应用场景。
一、使用filter方法过滤数组
filter() 方法创建一个新数组,新数组中的元素是通过检查指定数组中符合条件的所有元素。它不会改变原数组,而是返回一个过滤后的新数组。
基本语法如下:
let newArray = array.filter(callback(element, index, array), thisArg);
参数说明:
- callback:用于测试每个元素的函数,返回true则保留该元素,返回false则过滤掉
- element:当前正在处理的元素
- index(可选):当前元素的索引
- array(可选):调用filter方法的原数组
- thisArg(可选):执行callback时使用的this值
基础示例:过滤数字数组
假设我们有一个包含多个数字的数组,现在需要过滤出所有大于10的数字。
let numbers = [5, 12, 8, 130, 44, 3, 25];
// 使用filter方法过滤出大于10的元素
let filteredNumbers = numbers.filter(function(num) {
return num > 10;
});
console.log(filteredNumbers); // 输出: [12, 130, 44, 25]
console.log(numbers); // 原数组未被修改: [5, 12, 8, 130, 44, 3, 25]二、使用箭头函数简化过滤逻辑
配合ES6的箭头函数,可以让过滤代码更加简洁易读。
let numbers = [5, 12, 8, 130, 44, 3, 25]; // 使用箭头函数过滤 let filteredNumbers = numbers.filter(num => num > 10); console.log(filteredNumbers); // 输出: [12, 130, 44, 25]
箭头函数写法去掉了function关键字和大括号,使代码更加精简。
三、实际应用场景:过滤对象数组
在实际开发中,我们经常需要处理包含对象的数组。例如,从用户列表中过滤出活跃用户。
let users = [
{ id: 1, name: '张三', age: 25, active: true },
{ id: 2, name: '李四', age: 30, active: false },
{ id: 3, name: '王五', age: 22, active: true },
{ id: 4, name: '赵六', age: 35, active: false },
{ id: 5, name: '孙七', age: 28, active: true }
];
// 过滤出活跃用户
let activeUsers = users.filter(user => user.active === true);
console.log(activeUsers);
// 输出:
// [
// { id: 1, name: '张三', age: 25, active: true },
// { id: 3, name: '王五', age: 22, active: true },
// { id: 5, name: '孙七', age: 28, active: true }
// ]
// 过滤出年龄大于25的用户
let olderUsers = users.filter(user => user.age > 25);
console.log(olderUsers);
// 输出:
// [
// { id: 2, name: '李四', age: 30, active: false },
// { id: 4, name: '赵六', age: 35, active: false },
// { id: 5, name: '孙七', age: 28, active: true }
// ]四、使用索引进行过滤
filter方法的回调函数可以接收第二个参数index,我们有时候需要根据元素的位置来决定是否保留。
let numbers = [10, 20, 30, 40, 50, 60]; // 过滤出索引为偶数的元素 let evenIndexNumbers = numbers.filter((num, index) => index % 2 === 0); console.log(evenIndexNumbers); // 输出: [10, 30, 50] // 过滤出索引大于2的元素 let lastThreeNumbers = numbers.filter((num, index) => index > 2); console.log(lastThreeNumbers); // 输出: [40, 50, 60]
五、过滤去除空值和假值
在处理用户输入或API返回的数据时,经常需要去除数组中的空值、null、undefined等无效数据。
let messyArray = [0, 'hello', '', null, undefined, 'world', false, 42, NaN]; // 去除所有假值(false、null、undefined、0、""、NaN) let truthyValues = messyArray.filter(Boolean); console.log(truthyValues); // 输出: ['hello', 'world', 42] // 自定义过滤:保留非空字符串 let stringsOnly = messyArray.filter(item => typeof item === 'string' && item.length > 0); console.log(stringsOnly); // 输出: ['hello', 'world'] // 保留有效的数字 let validNumbers = messyArray.filter(item => typeof item === 'number' && !isNaN(item)); console.log(validNumbers); // 输出: [0, 42]
使用 filter(Boolean) 是一种非常简洁的写法,它会自动过滤掉所有假值。
六、结合indexOf实现去重过滤
利用filter方法的index参数,可以轻松实现数组去重。
let fruits = ['苹果', '香蕉', '苹果', '橘子', '香蕉', '葡萄', '苹果'];
// 使用filter去重
let uniqueFruits = fruits.filter((item, index, self) => {
return self.indexOf(item) === index;
});
console.log(uniqueFruits); // 输出: ['苹果', '香蕉', '橘子', '葡萄']这个原理是:对于每个元素,检查它在数组中第一次出现的位置是否等于当前索引,如果是则保留,否则说明是重复元素。
七、使用reduce方法实现过滤
虽然filter是最直接的过滤方法,但reduce() 也可以实现同样的功能,并且在需要同时进行转换操作时更加灵活。
let numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
// 使用reduce过滤出偶数
let evenNumbers = numbers.reduce((acc, num) => {
if (num % 2 === 0) {
acc.push(num);
}
return acc;
}, []);
console.log(evenNumbers); // 输出: [2, 4, 6, 8, 10]
// 使用reduce同时实现过滤和转换
let doubledEvenNumbers = numbers.reduce((acc, num) => {
if (num % 2 === 0) {
acc.push(num * 2);
}
return acc;
}, []);
console.log(doubledEvenNumbers); // 输出: [4, 8, 12, 16, 20]八、链式调用:组合多个过滤条件
filter方法返回的是一个新数组,因此可以连续调用多个filter,实现复合条件的过滤。
let products = [
{ name: '笔记本电脑', price: 5999, category: '电子', stock: 50 },
{ name: '手机', price: 3999, category: '电子', stock: 120 },
{ name: '沙发', price: 2999, category: '家具', stock: 30 },
{ name: '键盘', price: 299, category: '电子', stock: 200 },
{ name: '桌子', price: 999, category: '家具', stock: 15 },
{ name: '耳机', price: 599, category: '电子', stock: 80 }
];
// 链式调用过滤:电子类 + 价格小于1000 + 库存充足
let filteredProducts = products
.filter(product => product.category === '电子')
.filter(product => product.price < 1000)
.filter(product => product.stock > 50);
console.log(filteredProducts);
// 输出:
// [
// { name: '键盘', price: 299, category: '电子', stock: 200 },
// { name: '耳机', price: 599, category: '电子', stock: 80 }
// ]也可以在一个filter回调中组合多个条件,性能更好:
let products = [
{ name: '笔记本电脑', price: 5999, category: '电子', stock: 50 },
{ name: '手机', price: 3999, category: '电子', stock: 120 },
{ name: '沙发', price: 2999, category: '家具', stock: 30 },
{ name: '键盘', price: 299, category: '电子', stock: 200 },
{ name: '桌子', price: 999, category: '家具', stock: 15 },
{ name: '耳机', price: 599, category: '电子', stock: 80 }
];
// 单个filter中组合条件
let filteredProducts = products.filter(product => {
return product.category === '电子'
&& product.price < 1000
&& product.stock > 50;
});
console.log(filteredProducts);
// 输出相同结果九、使用传统for循环实现过滤
虽然filter方法很便捷,但了解传统循环的实现方式有助于理解过滤的本质。
let numbers = [5, 12, 8, 130, 44, 3, 25];
let result = [];
// 使用for循环过滤出大于10的数字
for (let i = 0; i < numbers.length; i++) {
if (numbers[i] > 10) {
result.push(numbers[i]);
}
}
console.log(result); // 输出: [12, 130, 44, 25]十、性能注意事项
在使用filter方法时,有几点值得注意:
- 不可变性:filter不会修改原数组,这是函数式编程的良好实践
- 空数组:如果原数组为空,filter返回一个空数组
- 稀疏数组:filter会跳过空槽位,不会对它们调用回调函数
- 回调中的this:如果使用普通函数,可以通过第二个参数指定this上下文
十一、总结
JavaScript的数组过滤主要通过filter()方法实现,它具有以下优点:
- 语法简洁,配合箭头函数可写出非常优雅的代码
- 不会改变原数组,保持数据不可变性
- 支持链式调用,方便组合多个过滤条件
- 回调函数提供元素、索引、原数组三个参数,灵活性高
掌握这些过滤技巧,能够帮助开发者更加高效地处理数组数据,在数据清洗、搜索筛选、条件匹配等场景中发挥作用。