Sitemap
myHotTake

myHotTake

Why Does JavaScript’s Reduce Struggle with Complex Data Transformations?

4 min readDec 21, 2024

--

Think of Array.prototype.reduce() as a very diligent delivery driver. Picture this: I’m managing a fleet of trucks, and each truck has a very specific job — pick up one package from each stop, then pass it to the next truck with instructions on how to update the cargo.

Now, this system works great if my instructions are clear and straightforward: “Add this package to the total weight” or “Count the number of fragile items.” Each truck simply picks up the package, follows my instructions, and passes the cargo along.

But what if I start asking my trucks to handle something much more complicated? Imagine I tell each driver, “At this stop, take the package, but also reorganize the entire truck’s cargo to fit the new shape of this package. Then, decide which items to keep or discard before moving on.” Suddenly, my trucks are overwhelmed. They weren’t built to manage this level of complexity — they’re just delivery trucks, not full-blown logistics centers.

This is what happens when I use reduce() for complex state transformations. It’s great at handling simple tasks where the instructions are clear and focused. But if I try to cram in too many responsibilities — tracking multiple pieces of state, splitting and merging data, or managing deeply nested structures — it becomes messy and hard to follow. My code turns into an unreadable pile of nested logic, like asking my drivers to reinvent the wheel at every stop.

For big logistics problems, I’d be better off using a specialized distribution center — maybe reduce() hands off the basic cargo to a helper function, or I use a different tool like map() or a dedicated state management library. That way, my fleet of trucks can stay fast, efficient, and laser-focused on what they do best.

Let’s bring our delivery truck analogy back into the world of JavaScript. Imagine I’ve got an array of packages, and I want to process them. If the task is straightforward, reduce() shines. But if it gets too complex, things can spiral out of control.

Get myHotTake’s stories in your inbox

Join Medium for free to get updates from this writer.

Here’s the simple use case first — our “delivery truck handles one package at a time” scenario:

const packages = [10, 20, 30, 40]; // weights of packages

// Simple task: Calculate the total weight of the packages
const totalWeight = packages.reduce((acc, weight) => acc + weight, 0);
console.log(totalWeight); // 100

This is perfect! Each truck just adds the package’s weight to the running total. The code is readable and efficient.

Enter Complexity

Now, suppose I want to handle something more intricate: say, grouping the packages by weight category and tracking counts. Suddenly, each truck needs to reorganize its cargo. Let’s try:

const packages = [10, 20, 30, 40];

// Complex task: Group packages into "light" and "heavy"
const categorized = packages.reduce((acc, weight) => {
if (weight <= 20) {
acc.light.push(weight);
} else {
acc.heavy.push(weight);
}
return acc;
}, { light: [], heavy: [] });
console.log(categorized);
// { light: [10, 20], heavy: [30, 40] }

This is manageable but already edging into “my trucks are doing too much” territory. I’ve added conditional logic and am mutating an accumulator object.

Overwhelmed Trucks

Now let’s go wild: what if I want to group the packages, calculate the total weight in each group, and tag each group with metadata?

const packages = [10, 20, 30, 40];

const result = packages.reduce((acc, weight) => {
const category = weight <= 20 ? "light" : "heavy";

if (!acc[category]) {
acc[category] = { count: 0, totalWeight: 0, details: [] };
}
acc[category].count += 1;
acc[category].totalWeight += weight;
acc[category].details.push(`Package: ${weight} lbs`);
return acc;
}, {});
console.log(result);
/*
{
light: {
count: 2,
totalWeight: 30,
details: ['Package: 10 lbs', 'Package: 20 lbs']
},
heavy: {
count: 2,
totalWeight: 70,
details: ['Package: 30 lbs', 'Package: 40 lbs']
}
}
*/

This works, but it’s starting to feel like asking my trucks to also manage customer service and warehouse inventory. The logic is now harder to read, debug, and extend.

A Better Approach

Instead of piling everything into reduce(), I could break this into smaller, more focused functions:

const categorizePackages = (packages) => {
return packages.reduce((acc, weight) => {
const category = weight <= 20 ? "light" : "heavy";
acc[category] = acc[category] || [];
acc[category].push(weight);
return acc;
}, {});
};

const calculateStats = (categories) => {
return Object.entries(categories).reduce((stats, [category, weights]) => {
stats[category] = {
count: weights.length,
totalWeight: weights.reduce((sum, w) => sum + w, 0),
details: weights.map((w) => `Package: ${w} lbs`),
};
return stats;
}, {});
};
const packages = [10, 20, 30, 40];
const categorized = categorizePackages(packages);
const stats = calculateStats(categorized);
console.log(stats);
// Same output as before, but logic is now modular and easier to follow

By splitting tasks, I keep each truck — or function — focused on its specific job. This makes the system cleaner, just like letting the logistics center handle the heavy lifting instead of overloading my trucks.

--

--