JavaScript中實現數組分組可通過多種方式,1.使用reduce方法:通過遍歷數組將元素按規則累積到結果對象中;2.使用foreach方法:邏輯類似但無需手動返回累加器;3.使用map對象:可保持鍵的插入順序。此外,分組依據支持復雜邏輯時可用函數定義分組規則,且分組后可進一步對每組數據進行聚合處理,如計算平均值或求和。性能上,reduce通常高效,而具體選擇需根據需求權衡。
JavaScript中實現數組分組,本質上是將一個數組按照某種規則拆分成多個子數組,并將這些子數組放入一個對象或Map中,key代表分組的依據,value就是分組后的子數組。
解決方案
實現數組分組的方法有很多,以下列舉幾種常用的方式:
立即學習“Java免費學習筆記(深入)”;
-
使用reduce方法: 這是最常見也是最靈活的方法。reduce可以遍歷數組,并根據自定義的邏輯將元素累積到一個結果對象中。
function groupBy(array, key) { return array.reduce((result, item) => { const groupKey = typeof key === 'function' ? key(item) : item[key]; if (!result[groupKey]) { result[groupKey] = []; } result[groupKey].push(item); return result; }, {}); } // 示例 const users = [ { id: 1, name: 'Alice', department: 'Engineering' }, { id: 2, name: 'Bob', department: 'Sales' }, { id: 3, name: 'Charlie', department: 'Engineering' }, { id: 4, name: 'David', department: 'Sales' }, ]; const groupedUsers = groupBy(users, 'department'); console.log(groupedUsers); // 輸出: // { // Engineering: [ // { id: 1, name: 'Alice', department: 'Engineering' }, // { id: 3, name: 'Charlie', department: 'Engineering' } // ], // Sales: [ // { id: 2, name: 'Bob', department: 'Sales' }, // { id: 4, name: 'David', department: 'Sales' } // ] // }
這段代碼的關鍵在于reduce的累加器(result)。 它初始化為一個空對象{}。 對于數組中的每個item,它計算groupKey。 如果result中還沒有以groupKey為鍵的屬性,就創建一個空數組[],然后將item添加到對應的數組中。
-
使用forEach方法: forEach相比reduce略微繁瑣,但更容易理解。
function groupByForEach(array, key) { const result = {}; array.forEach(item => { const groupKey = typeof key === 'function' ? key(item) : item[key]; if (!result[groupKey]) { result[groupKey] = []; } result[groupKey].push(item); }); return result; } // 示例 (同上) const users = [ { id: 1, name: 'Alice', department: 'Engineering' }, { id: 2, name: 'Bob', department: 'Sales' }, { id: 3, name: 'Charlie', department: 'Engineering' }, { id: 4, name: 'David', department: 'Sales' }, ]; const groupedUsersForEach = groupByForEach(users, 'department'); console.log(groupedUsersForEach);
forEach方法直接遍歷數組,邏輯與reduce類似,但不需要手動返回累加器。
-
使用Map對象: 如果需要保持分組順序,可以使用Map對象。
function groupByMap(array, key) { const result = new Map(); array.forEach(item => { const groupKey = typeof key === 'function' ? key(item) : item[key]; if (!result.has(groupKey)) { result.set(groupKey, []); } result.get(groupKey).push(item); }); return result; } // 示例 (同上) const users = [ { id: 1, name: 'Alice', department: 'Engineering' }, { id: 2, name: 'Bob', department: 'Sales' }, { id: 3, name: 'Charlie', department: 'Engineering' }, { id: 4, name: 'David', department: 'Sales' }, ]; const groupedUsersMap = groupByMap(users, 'department'); console.log(groupedUsersMap); // 輸出: // Map(2) { // 'Engineering' => [ // { id: 1, name: 'Alice', department: 'Engineering' }, // { id: 3, name: 'Charlie', department: 'Engineering' } // ], // 'Sales' => [ // { id: 2, name: 'Bob', department: 'Sales' }, // { id: 4, name: 'David', department: 'Sales' } // ] // }
Map對象會記住鍵的插入順序。 這在某些場景下非常重要。
如何處理分組依據是復雜邏輯的情況?
當分組依據不僅僅是對象的某個屬性,而需要進行一些復雜的計算時,可以將key參數設置為一個函數。 這個函數接收數組的每個元素作為參數,并返回分組的依據。
const products = [ { id: 1, name: 'Apple', price: 1.0 }, { id: 2, name: 'Banana', price: 0.5 }, { id: 3, name: 'Orange', price: 0.8 }, { id: 4, name: 'Grapes', price: 2.0 }, ]; const groupedByPriceRange = groupBy(products, product => { if (product.price < 1.0) { return 'Cheap'; } else { return 'Expensive'; } }); console.log(groupedByPriceRange); // 輸出: // { // Cheap: [ // { id: 2, name: 'Banana', price: 0.5 }, // { id: 3, name: 'Orange', price: 0.8 } // ], // Expensive: [ // { id: 1, name: 'Apple', price: 1.0 }, // { id: 4, name: 'Grapes', price: 2.0 } // ] // }
如何處理分組后需要對每個分組進行進一步處理的情況?
有時候,分組僅僅是第一步,接下來可能需要對每個分組進行一些聚合操作,例如計算平均值、求和等。 可以在分組之后,使用Object.entries()方法將分組結果轉換為鍵值對數組,然后使用map方法對每個分組進行處理。
const students = [ { name: 'Alice', grade: 'A', score: 90 }, { name: 'Bob', grade: 'B', score: 80 }, { name: 'Charlie', grade: 'A', score: 95 }, { name: 'David', grade: 'B', score: 85 }, ]; const groupedByGrade = groupBy(students, 'grade'); const averageScores = Object.entries(groupedByGrade).map(([grade, students]) => { const totalScore = students.reduce((sum, student) => sum + student.score, 0); const averageScore = totalScore / students.length; return { grade, averageScore }; }); console.log(averageScores); // 輸出: // [ // { grade: 'A', averageScore: 92.5 }, // { grade: 'B', averageScore: 82.5 } // ]
性能考慮:哪種方法更高效?
通常來說,reduce方法在大多數情況下性能都比較好,因為它可以在一次遍歷中完成分組。 forEach方法在某些情況下可能更易讀,但性能差異通常可以忽略不計。 如果需要保持分組順序,并且數據量不是特別大,Map對象也是一個不錯的選擇。 具體選擇哪種方法,需要根據實際情況進行權衡。 如果對性能有極致要求,可以進行基準測試,選擇最適合自己場景的方法。 此外,如果數組非常大,可以考慮使用Web Workers進行并行處理,以提高性能。