groupBy()
Group array items by a provided function or key for data organization and analysis.
Utility Function
Arrays
Grouping
Syntax
groupBy(array, keyFn)Parameters
array Array
The array to group.
keyFn Function|string
Function that returns the grouping key, or string property name.
Returns
Object
- Object with grouped arrays as values.Examples
Basic Grouping by Property
const users = [
{ name: "Alice", department: "Engineering", age: 28 },
{ name: "Bob", department: "Marketing", age: 32 },
{ name: "Charlie", department: "Engineering", age: 25 },
{ name: "Diana", department: "Sales", age: 29 },
{ name: "Eve", department: "Marketing", age: 31 }
];
// Group by department using property name
const byDepartment = groupBy(users, 'department');
console.log(byDepartment);
// {
// Engineering: [
// { name: "Alice", department: "Engineering", age: 28 },
// { name: "Charlie", department: "Engineering", age: 25 }
// ],
// Marketing: [
// { name: "Bob", department: "Marketing", age: 32 },
// { name: "Eve", department: "Marketing", age: 31 }
// ],
// Sales: [
// { name: "Diana", department: "Sales", age: 29 }
// ]
// }Grouping with Custom Function
const products = [
{ name: "Laptop", price: 999, category: "Electronics" },
{ name: "Book", price: 15, category: "Education" },
{ name: "Phone", price: 699, category: "Electronics" },
{ name: "Tablet", price: 299, category: "Electronics" },
{ name: "Notebook", price: 5, category: "Education" }
];
// Group by price range
const byPriceRange = groupBy(products, (product) => {
if (product.price < 50) return "Budget";
if (product.price < 500) return "Mid-range";
return "Premium";
});
console.log(byPriceRange);
// {
// Budget: [
// { name: "Book", price: 15, category: "Education" },
// { name: "Notebook", price: 5, category: "Education" }
// ],
// "Mid-range": [
// { name: "Tablet", price: 299, category: "Electronics" }
// ],
// Premium: [
// { name: "Laptop", price: 999, category: "Electronics" },
// { name: "Phone", price: 699, category: "Electronics" }
// ]
// }Grouping by Multiple Criteria
const orders = [
{ id: 1, status: "pending", priority: "high", region: "US" },
{ id: 2, status: "completed", priority: "low", region: "EU" },
{ id: 3, status: "pending", priority: "high", region: "EU" },
{ id: 4, status: "processing", priority: "medium", region: "US" },
{ id: 5, status: "completed", priority: "high", region: "US" }
];
// Group by status and priority combination
const byStatusPriority = groupBy(orders, (order) =>
`${order.status}-${order.priority}`
);
console.log(byStatusPriority);
// {
// "pending-high": [
// { id: 1, status: "pending", priority: "high", region: "US" },
// { id: 3, status: "pending", priority: "high", region: "EU" }
// ],
// "completed-low": [
// { id: 2, status: "completed", priority: "low", region: "EU" }
// ],
// "processing-medium": [
// { id: 4, status: "processing", priority: "medium", region: "US" }
// ],
// "completed-high": [
// { id: 5, status: "completed", priority: "high", region: "US" }
// ]
// }Grouping by Date Periods
const events = [
{ name: "Meeting A", date: "2023-01-15", type: "meeting" },
{ name: "Conference", date: "2023-01-20", type: "conference" },
{ name: "Meeting B", date: "2023-02-05", type: "meeting" },
{ name: "Workshop", date: "2023-02-10", type: "workshop" },
{ name: "Meeting C", date: "2023-03-01", type: "meeting" }
];
// Group by month
const byMonth = groupBy(events, (event) => {
const date = new Date(event.date);
return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}`;
});
console.log(byMonth);
// {
// "2023-01": [
// { name: "Meeting A", date: "2023-01-15", type: "meeting" },
// { name: "Conference", date: "2023-01-20", type: "conference" }
// ],
// "2023-02": [
// { name: "Meeting B", date: "2023-02-05", type: "meeting" },
// { name: "Workshop", date: "2023-02-10", type: "workshop" }
// ],
// "2023-03": [
// { name: "Meeting C", date: "2023-03-01", type: "meeting" }
// ]
// }
// Group by quarter
const byQuarter = groupBy(events, (event) => {
const date = new Date(event.date);
const quarter = Math.ceil((date.getMonth() + 1) / 3);
return `Q${quarter} ${date.getFullYear()}`;
});
console.log(byQuarter);
// {
// "Q1 2023": [
// { name: "Meeting A", date: "2023-01-15", type: "meeting" },
// { name: "Conference", date: "2023-01-20", type: "conference" },
// { name: "Meeting B", date: "2023-02-05", type: "meeting" },
// { name: "Workshop", date: "2023-02-10", type: "workshop" },
// { name: "Meeting C", date: "2023-03-01", type: "meeting" }
// ]
// }Data Analysis with Grouping
const sales = [
{ product: "Laptop", amount: 1200, salesperson: "Alice", region: "North" },
{ product: "Phone", amount: 800, salesperson: "Bob", region: "South" },
{ product: "Tablet", amount: 400, salesperson: "Alice", region: "North" },
{ product: "Laptop", amount: 1200, salesperson: "Charlie", region: "East" },
{ product: "Phone", amount: 800, salesperson: "Diana", region: "West" }
];
class SalesAnalyzer {
constructor(salesData) {
this.data = salesData;
}
byRegion() {
return groupBy(this.data, 'region');
}
bySalesperson() {
return groupBy(this.data, 'salesperson');
}
byProduct() {
return groupBy(this.data, 'product');
}
getRegionTotals() {
const grouped = this.byRegion();
const totals = {};
for (const [region, sales] of Object.entries(grouped)) {
totals[region] = {
totalAmount: sales.reduce((sum, sale) => sum + sale.amount, 0),
salesCount: sales.length,
averageAmount: sales.reduce((sum, sale) => sum + sale.amount, 0) / sales.length
};
}
return totals;
}
getTopPerformers() {
const grouped = this.bySalesperson();
const performance = {};
for (const [person, sales] of Object.entries(grouped)) {
performance[person] = {
totalSales: sales.reduce((sum, sale) => sum + sale.amount, 0),
salesCount: sales.length
};
}
return Object.entries(performance)
.sort(([,a], [,b]) => b.totalSales - a.totalSales)
.reduce((obj, [person, stats]) => {
obj[person] = stats;
return obj;
}, {});
}
}
const analyzer = new SalesAnalyzer(sales);
console.log("By Region:", analyzer.byRegion());
console.log("Region Totals:", analyzer.getRegionTotals());
// {
// North: { totalAmount: 1600, salesCount: 2, averageAmount: 800 },
// South: { totalAmount: 800, salesCount: 1, averageAmount: 800 },
// East: { totalAmount: 1200, salesCount: 1, averageAmount: 1200 },
// West: { totalAmount: 800, salesCount: 1, averageAmount: 800 }
// }
console.log("Top Performers:", analyzer.getTopPerformers());
// {
// Alice: { totalSales: 1600, salesCount: 2 },
// Charlie: { totalSales: 1200, salesCount: 1 },
// Bob: { totalSales: 800, salesCount: 1 },
// Diana: { totalSales: 800, salesCount: 1 }
// }Log Analysis
const logs = [
{ timestamp: "2023-01-01T10:00:00Z", level: "INFO", message: "Server started", service: "api" },
{ timestamp: "2023-01-01T10:05:00Z", level: "ERROR", message: "Database connection failed", service: "api" },
{ timestamp: "2023-01-01T10:10:00Z", level: "WARN", message: "High memory usage", service: "worker" },
{ timestamp: "2023-01-01T10:15:00Z", level: "INFO", message: "Task completed", service: "worker" },
{ timestamp: "2023-01-01T10:20:00Z", level: "ERROR", message: "Authentication failed", service: "auth" }
];
class LogAnalyzer {
constructor(logs) {
this.logs = logs;
}
byLevel() {
return groupBy(this.logs, 'level');
}
byService() {
return groupBy(this.logs, 'service');
}
byHour() {
return groupBy(this.logs, (log) => {
const date = new Date(log.timestamp);
return `${date.getHours()}:00`;
});
}
getErrorSummary() {
const errorLogs = this.byLevel()['ERROR'] || [];
return groupBy(errorLogs, 'service');
}
getServiceHealth() {
const byService = this.byService();
const health = {};
for (const [service, logs] of Object.entries(byService)) {
const errorCount = logs.filter(log => log.level === 'ERROR').length;
const warnCount = logs.filter(log => log.level === 'WARN').length;
const totalCount = logs.length;
health[service] = {
total: totalCount,
errors: errorCount,
warnings: warnCount,
errorRate: (errorCount / totalCount * 100).toFixed(2) + '%',
status: errorCount === 0 ? 'healthy' : errorCount > totalCount * 0.5 ? 'critical' : 'warning'
};
}
return health;
}
}
const logAnalyzer = new LogAnalyzer(logs);
console.log("By Level:", logAnalyzer.byLevel());
console.log("Error Summary:", logAnalyzer.getErrorSummary());
// {
// api: [
// { timestamp: "2023-01-01T10:05:00Z", level: "ERROR", message: "Database connection failed", service: "api" }
// ],
// auth: [
// { timestamp: "2023-01-01T10:20:00Z", level: "ERROR", message: "Authentication failed", service: "auth" }
// ]
// }
console.log("Service Health:", logAnalyzer.getServiceHealth());
// {
// api: { total: 2, errors: 1, warnings: 0, errorRate: "50.00%", status: "critical" },
// worker: { total: 2, errors: 0, warnings: 1, errorRate: "0.00%", status: "healthy" },
// auth: { total: 1, errors: 1, warnings: 0, errorRate: "100.00%", status: "critical" }
// }Survey Data Processing
const surveyResponses = [
{ id: 1, age: 25, gender: "F", satisfaction: 4, department: "Engineering" },
{ id: 2, age: 32, gender: "M", satisfaction: 5, department: "Marketing" },
{ id: 3, age: 28, gender: "F", satisfaction: 3, department: "Engineering" },
{ id: 4, age: 45, gender: "M", satisfaction: 4, department: "Sales" },
{ id: 5, age: 29, gender: "F", satisfaction: 5, department: "Marketing" },
{ id: 6, age: 35, gender: "M", satisfaction: 2, department: "Engineering" }
];
class SurveyAnalyzer {
constructor(responses) {
this.responses = responses;
}
byDemographic(field) {
return groupBy(this.responses, field);
}
byAgeGroup() {
return groupBy(this.responses, (response) => {
const age = response.age;
if (age < 30) return "20-29";
if (age < 40) return "30-39";
if (age < 50) return "40-49";
return "50+";
});
}
bySatisfactionLevel() {
return groupBy(this.responses, (response) => {
const score = response.satisfaction;
if (score <= 2) return "Dissatisfied";
if (score <= 3) return "Neutral";
if (score <= 4) return "Satisfied";
return "Very Satisfied";
});
}
getDepartmentSatisfaction() {
const byDept = this.byDemographic('department');
const satisfaction = {};
for (const [dept, responses] of Object.entries(byDept)) {
const scores = responses.map(r => r.satisfaction);
const average = scores.reduce((sum, score) => sum + score, 0) / scores.length;
satisfaction[dept] = {
averageScore: Math.round(average * 100) / 100,
responseCount: responses.length,
distribution: this.getScoreDistribution(scores)
};
}
return satisfaction;
}
getScoreDistribution(scores) {
const distribution = { 1: 0, 2: 0, 3: 0, 4: 0, 5: 0 };
scores.forEach(score => distribution[score]++);
return distribution;
}
getCrossTabulation(field1, field2) {
const result = {};
this.responses.forEach(response => {
const key1 = response[field1];
const key2 = response[field2];
if (!result[key1]) result[key1] = {};
if (!result[key1][key2]) result[key1][key2] = 0;
result[key1][key2]++;
});
return result;
}
}
const surveyAnalyzer = new SurveyAnalyzer(surveyResponses);
console.log("By Age Group:", surveyAnalyzer.byAgeGroup());
console.log("By Satisfaction:", surveyAnalyzer.bySatisfactionLevel());
console.log("Department Satisfaction:", surveyAnalyzer.getDepartmentSatisfaction());
// {
// Engineering: {
// averageScore: 3.33,
// responseCount: 3,
// distribution: { 1: 0, 2: 1, 3: 1, 4: 1, 5: 0 }
// },
// Marketing: {
// averageScore: 5,
// responseCount: 2,
// distribution: { 1: 0, 2: 0, 3: 0, 4: 0, 5: 2 }
// },
// Sales: {
// averageScore: 4,
// responseCount: 1,
// distribution: { 1: 0, 2: 0, 3: 0, 4: 1, 5: 0 }
// }
// }
console.log("Gender vs Department:", surveyAnalyzer.getCrossTabulation('gender', 'department'));
// {
// F: { Engineering: 2, Marketing: 1 },
// M: { Marketing: 1, Sales: 1, Engineering: 1 }
// }