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:

  1. Grouping Phase: Use reduce() to create groups using a composite key (SupplierName + Category)
  2. Calculation Phase: Transform groups into final format with calculated averages

Key features:

  • Handles undefined values 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.

Updated on: 2026-03-15T23:19:00+05:30

1K+ Views

Kickstart Your Career

Get certified by completing the course

Get Started
Advertisements