數組高階方法:map、filter、reduce實戰指南
文章簡介
在HarmonyOS應用開發中,數組操作是日常開發的重要組成部分。本文將深入探討三個核心的數組高階方法:map、filter和reduce,幫助開發者掌握這些強大的數據處理工具。
官方參考資料:
- ArkTS語言介紹
- MDN-數組
版本説明:本文所有示例基於HarmonyOS Next API 10+ 和 DevEco Studio 4.0+
前置知識
在開始學習高階方法之前,確保你已瞭解以下基礎概念:
- 數組的基本操作
- 箭頭函數語法
- 類型註解基礎
- 基本的TS/JS語法
1. map方法詳解
1.1 基礎概念
map方法用於遍歷數組並對每個元素執行指定操作,返回一個新數組。
核心特性:
- 不改變原數組
- 返回新數組長度與原數組相同
- 適合數據轉換場景
1.2 基本語法
// 基礎語法
const newArray = originalArray.map((currentValue, index, array) => {
// 返回處理後的元素
});
1.3 實戰示例
示例1:數值數組轉換
// 將價格數組轉換為含税價格(税率10%)
const prices: number[] = [100, 200, 300, 400];
const pricesWithTax = prices.map(price => price * 1.1);
console.log(pricesWithTax); // [110, 220, 330, 440]
示例2:對象數組屬性提取
interface User {
id: number;
name: string;
age: number;
}
const users: User[] = [
{ id: 1, name: "張三", age: 25 },
{ id: 2, name: "李四", age: 30 },
{ id: 3, name: "王五", age: 28 }
];
// 提取用户姓名數組
const userNames = users.map(user => user.name);
console.log(userNames); // ["張三", "李四", "王五"]
// 添加新屬性
const usersWithStatus = users.map(user => ({
...user,
isActive: user.age > 25
}));
1.4 map方法參數詳解
|
參數 |
類型 |
描述 |
是否必選 |
|
callback |
function |
對每個元素執行的函數 |
是 |
|
currentValue |
any |
當前處理的元素 |
- |
|
index |
number |
當前元素的索引 |
- |
|
array |
Array |
調用map的數組本身 |
- |
|
thisArg |
any |
執行callback時的this值 |
否 |
2. filter方法詳解
2.1 基礎概念
filter方法用於篩選數組中滿足條件的元素,返回新數組。
核心特性:
- 返回數組長度 ≤ 原數組長度
- 不會改變原數組
- 適合數據篩選場景
2.2 基本語法
const filteredArray = originalArray.filter((currentValue, index, array) => {
// 返回true保留元素,false過濾元素
});
2.3 實戰示例
示例1:基礎數據篩選
// 篩選偶數
const numbers: number[] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
const evenNumbers = numbers.filter(num => num % 2 === 0);
console.log(evenNumbers); // [2, 4, 6, 8, 10]
示例2:複雜對象篩選
interface Product {
id: number;
name: string;
price: number;
category: string;
inStock: boolean;
}
const products: Product[] = [
{ id: 1, name: "手機", price: 2999, category: "electronics", inStock: true },
{ id: 2, name: "書籍", price: 59, category: "education", inStock: false },
{ id: 3, name: "耳機", price: 399, category: "electronics", inStock: true },
{ id: 4, name: "筆記本", price: 15, category: "office", inStock: true }
];
// 篩選有庫存的電子產品
const availableElectronics = products.filter(product =>
product.category === "electronics" && product.inStock
);
console.log(availableElectronics);
// [{ id: 1, name: "手機", ... }, { id: 3, name: "耳機", ... }]
2.4 多條件篩選技巧
// 價格範圍篩選
const priceRangeFilter = (minPrice: number, maxPrice: number) => {
return products.filter(product =>
product.price >= minPrice && product.price <= maxPrice
);
};
const affordableProducts = priceRangeFilter(50, 500);
console.log(affordableProducts); // 價格在50-500之間的產品
3. reduce方法詳解
3.1 基礎概念
reduce方法將數組元素通過 reducer 函數累積為單個值。
核心特性:
- 返回任意類型的單個值
- 功能最強大的數組方法
- 適合聚合計算場景
3.2 基本語法
const result = array.reduce((accumulator, currentValue, index, array) => {
// 返回累積值
}, initialValue);
3.3 實戰示例
示例1:數值計算
// 計算數組總和
const numbers: number[] = [1, 2, 3, 4, 5];
const sum = numbers.reduce((acc, curr) => acc + curr, 0);
console.log(sum); // 15
// 找出最大值
const max = numbers.reduce((acc, curr) => Math.max(acc, curr), numbers[0]);
console.log(max); // 5
示例2:複雜數據聚合
interface OrderItem {
product: string;
quantity: number;
price: number;
}
const orderItems: OrderItem[] = [
{ product: "手機", quantity: 1, price: 2999 },
{ product: "耳機", quantity: 2, price: 399 },
{ product: "保護殼", quantity: 1, price: 59 }
];
// 計算訂單總金額
const totalAmount = orderItems.reduce((total, item) => {
return total + (item.quantity * item.price);
}, 0);
console.log(totalAmount); // 3856
// 按產品分類統計
const productStats = orderItems.reduce((stats, item) => {
if (!stats[item.product]) {
stats[item.product] = { totalQuantity: 0, totalRevenue: 0 };
}
stats[item.product].totalQuantity += item.quantity;
stats[item.product].totalRevenue += item.quantity * item.price;
return stats;
}, {} as Record<string, { totalQuantity: number; totalRevenue: number }>);
4. 方法鏈式組合實戰
4.1 數據處理管道
在實際開發中,我們經常需要組合使用這些方法:
interface Employee {
id: number;
name: string;
department: string;
salary: number;
yearsOfExperience: number;
}
const employees: Employee[] = [
{ id: 1, name: "張三", department: "技術部", salary: 15000, yearsOfExperience: 3 },
{ id: 2, name: "李四", department: "技術部", salary: 18000, yearsOfExperience: 5 },
{ id: 3, name: "王五", department: "市場部", salary: 12000, yearsOfExperience: 2 },
{ id: 4, name: "趙六", department: "技術部", salary: 22000, yearsOfExperience: 8 },
{ id: 5, name: "錢七", department: "人事部", salary: 10000, yearsOfExperience: 1 }
];
// 複雜數據處理:技術部員工,經驗3年以上,提取姓名和調整後薪資(+10%)
const processedData = employees
.filter(emp => emp.department === "技術部" && emp.yearsOfExperience >= 3)
.map(emp => ({
name: emp.name,
adjustedSalary: Math.round(emp.salary * 1.1), // 薪資調整10%
experience: emp.yearsOfExperience
}))
.reduce((result, emp) => {
result.totalAdjustedSalary += emp.adjustedSalary;
result.employees.push(emp);
return result;
}, { totalAdjustedSalary: 0, employees: [] as Array<{name: string; adjustedSalary: number; experience: number}> });
console.log(processedData);
4.2 性能優化技巧
// 避免在循環中重複計算
const optimizedProcess = (data: Employee[]) => {
// 先過濾,減少後續處理的數據量
return data
.filter(emp => emp.department === "技術部")
.map(emp => {
// 複雜計算只執行一次
const bonus = calculateBonus(emp.yearsOfExperience);
const adjustedSalary = emp.salary + bonus;
return {
...emp,
adjustedSalary,
bonus
};
});
};
// 模擬獎金計算函數
const calculateBonus = (experience: number): number => {
return experience * 500; // 每年經驗500元獎金
};
5. HarmonyOS特定應用場景
5.1 UI數據綁定
在HarmonyOS的ArkUI開發中,數組方法常用於數據處理:
// 在HarmonyOS組件中使用
@Component
struct ProductList {
@State products: Product[] = [
{ id: 1, name: "HarmonyOS手機", price: 3999, category: "electronics", inStock: true },
{ id: 2, name: "智能手錶", price: 1299, category: "electronics", inStock: true },
{ id: 3, name: "平板電腦", price: 2599, category: "electronics", inStock: false }
];
build() {
Column() {
// 使用filter和map準備顯示數據
ForEach(this.products
.filter(product => product.inStock)
.map(product => ({ ...product, displayPrice: `¥${product.price}` })),
(item: Product & { displayPrice: string }) => {
Text(item.name)
.fontSize(16)
Text(item.displayPrice)
.fontSize(14)
.fontColor(Color.Gray)
}
)
}
}
}
5.2 狀態管理數據處理
// 在AppStorage或狀態管理中處理數組數據
class ShoppingCartService {
private items: CartItem[] = [];
// 添加商品到購物車
addItem(newItem: CartItem) {
this.items = [...this.items, newItem];
}
// 計算總價
getTotalPrice(): number {
return this.items.reduce((total, item) => total + item.price * item.quantity, 0);
}
// 獲取商品種類數量
getUniqueProductCount(): number {
return this.items
.map(item => item.productId)
.filter((productId, index, array) => array.indexOf(productId) === index)
.length;
}
// 按分類分組
getItemsByCategory() {
return this.items.reduce((groups, item) => {
const category = item.category;
if (!groups[category]) {
groups[category] = [];
}
groups[category].push(item);
return groups;
}, {} as Record<string, CartItem[]>);
}
}
6. 性能考慮和最佳實踐
6.1 性能優化建議
避免的陷阱:
- 不要在render或build方法中進行復雜計算
- 避免創建不必要的中間數組
- 合理使用緩存機制
// 不好的做法:每次渲染都重新計算
@Component
struct BadExample {
@State data: number[] = [1, 2, 3, 4, 5];
build() {
Column() {
// 每次build都會重新計算
ForEach(this.data.map(x => x * 2), (item: number) => {
Text(item.toString())
})
}
}
}
// 好的做法:使用計算屬性或緩存
@Component
struct GoodExample {
@State data: number[] = [1, 2, 3, 4, 5];
private cachedData: number[] = [];
// 使用aboutToAppear進行預處理
aboutToAppear() {
this.cachedData = this.data.map(x => x * 2);
}
build() {
Column() {
ForEach(this.cachedData, (item: number) => {
Text(item.toString())
})
}
}
}
6.2 錯誤處理
// 安全的數組操作
const safeArrayOperations = <T>(array: T[] | null | undefined) => {
// 處理可能的空值或未定義
const safeArray = array || [];
return {
mapped: safeArray.map(item => item),
filtered: safeArray.filter(item => !!item),
reduced: safeArray.reduce((acc, curr) => acc, 0)
};
};
// 在HarmonyOS組件中的錯誤邊界處理
@Component
struct SafeArrayComponent {
@State data: number[] | null = null;
build() {
Column() {
// 使用可選鏈和空值合併
ForEach((this.data ?? []).map(item => item * 2), (item: number) => {
Text(item.toString())
})
}
}
}
7. 注意事項和重要提示
7.1 常見陷阱
map方法注意事項:
- 一定要有return語句,否則會得到undefined數組
- 不要在有副作用的操作中使用map
- 確保回調函數是純函數
// 錯誤示例
const wrongMap = numbers.map(num => {
console.log(num); // 副作用!
// 缺少return語句
});
// 正確做法
const correctMap = numbers.map(num => num * 2);
filter方法注意事項:
- 回調函數必須返回boolean值
- 注意處理空數組情況
- 考慮使用類型守衞
// 使用類型守衞
const mixedArray: (number | string | null)[] = [1, "hello", null, 2, "world"];
// 只保留數字類型
const numbersOnly = mixedArray.filter((item): item is number =>
typeof item === "number"
);
reduce方法注意事項:
- 不要忘記提供初始值
- 確保累積器和當前值類型一致
- 在複雜對象reduce時注意引用問題
// 提供正確的初始值類型
interface Accumulator {
sum: number;
count: number;
}
const stats = numbers.reduce<Accumulator>((acc, curr) => {
return {
sum: acc.sum + curr,
count: acc.count + 1
};
}, { sum: 0, count: 0 }); // 明確的初始值
7.2 性能監控
在大型數組操作時,建議添加性能監控:
const measurePerformance = <T>(operation: string, fn: () => T): T => {
const start = Date.now();
const result = fn();
const end = Date.now();
console.log(`${operation} 耗時: ${end - start}ms`);
return result;
};
// 使用示例
const largeArray = Array.from({ length: 10000 }, (_, i) => i);
const result = measurePerformance('map操作', () =>
largeArray.map(x => x * 2)
);
總結
通過本文的學習,你應該已經掌握了:
- map:用於數據轉換,1:1映射
- filter:用於數據篩選,返回子集
- reduce:用於數據聚合,返回單個值
- 方法鏈:組合使用實現複雜數據處理
這些高階方法在HarmonyOS應用開發中極其重要,特別是在:
- UI數據準備
- 狀態管理
- 業務邏輯處理
- 數據格式化
實踐建議:
- 在簡單循環場景優先考慮高階方法
- 注意性能,避免不必要的中間數組
- 合理使用方法