Article Categories
- All Categories
-
Data Structure
-
Networking
-
RDBMS
-
Operating System
-
Java
-
MS Excel
-
iOS
-
HTML
-
CSS
-
Android
-
Python
-
C Programming
-
C++
-
C#
-
MongoDB
-
MySQL
-
Javascript
-
PHP
-
Economics & Finance
Selected Reading
Calculate average from JSON data based on multiple filters JavaScript
When working with JSON data, you often need to filter, group, and calculate averages based on multiple criteria. This article demonstrates how to group objects by multiple fields and compute averages while handling edge cases like undefined values.
Problem Statement
Given an array of supplier objects, we need to:
- Group objects with the same "SupplierName" and "Category"
- Sum their points together (ignoring undefined values)
- Calculate the average points for each group
- Return the grouped results with totals and averages
Input Data
const arr = [
{ "SupplierName": "John", "Category": "A", "Points": 3 },
{ "SupplierName": "John", "Category": "A", "Points": 11 },
{ "SupplierName": "John", "Category": "A", "Points": undefined },
{ "SupplierName": "John", "Category": "B", "Points": 2 },
{ "SupplierName": "John", "Category": "B", "Points": 6 },
{ "SupplierName": "Praveen", "Category": "A", "Points": 3 },
{ "SupplierName": "Praveen", "Category": "A", "Points": 7 }
];
Expected Output
[
{ "SupplierName": "John", "Category": "A", "Points": 14, "Average": 7 },
{ "SupplierName": "John", "Category": "B", "Points": 8, "Average": 4 },
{ "SupplierName": "Praveen", "Category": "A", "Points": 10, "Average": 5 }
]
Solution Using reduce() Method
const arr = [
{ "SupplierName": "John", "Category": "A", "Points": 3 },
{ "SupplierName": "John", "Category": "A", "Points": 11 },
{ "SupplierName": "John", "Category": "A", "Points": undefined },
{ "SupplierName": "John", "Category": "B", "Points": 2 },
{ "SupplierName": "John", "Category": "B", "Points": 6 },
{ "SupplierName": "Praveen", "Category": "A", "Points": 3 },
{ "SupplierName": "Praveen", "Category": "A", "Points": 7 }
];
const groupAndAverage = (arr = []) => {
// First phase: group by supplier name and category
const groups = arr.reduce((acc, obj) => {
const key = obj.SupplierName + obj.Category;
if (acc[key]) {
// Add points if they exist and increment counter
if (obj.Points !== undefined) {
acc[key].Points += obj.Points;
acc[key].count++;
}
} else {
// Create new group entry
acc[key] = {
SupplierName: obj.SupplierName,
Category: obj.Category,
Points: obj.Points || 0,
count: obj.Points !== undefined ? 1 : 0
};
}
return acc;
}, {});
// Second phase: calculate averages
const result = Object.values(groups).map(group => ({
SupplierName: group.SupplierName,
Category: group.Category,
Points: group.Points,
Average: group.count > 0 ? Math.round(group.Points / group.count) : 0
}));
return result;
};
console.log(JSON.stringify(groupAndAverage(arr), null, 2));
[
{
"SupplierName": "John",
"Category": "A",
"Points": 14,
"Average": 7
},
{
"SupplierName": "John",
"Category": "B",
"Points": 8,
"Average": 4
},
{
"SupplierName": "Praveen",
"Category": "A",
"Points": 10,
"Average": 5
}
]
How It Works
The solution uses a two-phase approach:
-
Grouping Phase: Use
reduce()to create groups using a composite key (SupplierName + Category) - Calculation Phase: Transform groups into final format with calculated averages
Key features:
- Handles
undefinedvalues by skipping them in calculations - Uses a separate counter to track valid entries for accurate averaging
- Creates clean output objects without internal tracking properties
Alternative Approach Using Map
const groupAndAverageWithMap = (arr = []) => {
const groups = new Map();
arr.forEach(obj => {
const key = `${obj.SupplierName}-${obj.Category}`;
if (groups.has(key)) {
const existing = groups.get(key);
if (obj.Points !== undefined) {
existing.totalPoints += obj.Points;
existing.count++;
}
} else {
groups.set(key, {
SupplierName: obj.SupplierName,
Category: obj.Category,
totalPoints: obj.Points || 0,
count: obj.Points !== undefined ? 1 : 0
});
}
});
return Array.from(groups.values()).map(group => ({
SupplierName: group.SupplierName,
Category: group.Category,
Points: group.totalPoints,
Average: group.count > 0 ? Math.round(group.totalPoints / group.count) : 0
}));
};
console.log("Using Map approach:");
console.log(JSON.stringify(groupAndAverageWithMap(arr), null, 2));
Using Map approach:
[
{
"SupplierName": "John",
"Category": "A",
"Points": 14,
"Average": 7
},
{
"SupplierName": "John",
"Category": "B",
"Points": 8,
"Average": 4
},
{
"SupplierName": "Praveen",
"Category": "A",
"Points": 10,
"Average": 5
}
]
Conclusion
Both approaches effectively group JSON data by multiple filters and calculate averages. The reduce() method is more functional, while Map offers cleaner key handling. Choose based on your preference and performance requirements.
Advertisements
