Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Aggregations

Combine many items into one result.

reduce()

The Swiss Army knife of aggregations:

import { enumerate } from "jsr:@j50n/proc@0.23.3";

const sum = await enumerate([1, 2, 3, 4])
  .reduce((acc, n) => acc + n, 0);
// 10

How It Works

// Start with initial value: 0
// Step 1: 0 + 1 = 1
// Step 2: 1 + 2 = 3
// Step 3: 3 + 3 = 6
// Step 4: 6 + 4 = 10

Building Objects

const grouped = await enumerate(items)
  .reduce((acc, item) => {
    const key = item.category;
    acc[key] = acc[key] || [];
    acc[key].push(item);
    return acc;
  }, {});

Calculating Statistics

const stats = await enumerate(numbers)
  .reduce((acc, n) => ({
    sum: acc.sum + n,
    count: acc.count + 1,
    min: Math.min(acc.min, n),
    max: Math.max(acc.max, n)
  }), { sum: 0, count: 0, min: Infinity, max: -Infinity });

const average = stats.sum / stats.count;

count()

Count items:

const total = await enumerate([1, 2, 3, 4, 5]).count();
// 5

Count Matches

const errorCount = await read("app.log")
  .lines
  .filter(line => line.includes("ERROR"))
  .count();

some()

Check if any item matches:

const hasError = await enumerate(lines)
  .some(line => line.includes("ERROR"));
// true or false

Early Exit

Stops as soon as it finds a match:

// Stops reading after first match
const hasLargeFile = await enumerate(files)
  .some(file => file.size > 1_000_000_000);

every()

Check if all items match:

const allPositive = await enumerate([1, 2, 3, 4])
  .every(n => n > 0);
// true

Validation

const allValid = await enumerate(records)
  .every(record => 
    record.name && 
    record.email && 
    record.age >= 0
  );

find()

Find first matching item:

const firstError = await enumerate(lines)
  .find(line => line.includes("ERROR"));
// First line with ERROR, or undefined

With Complex Predicate

const admin = await enumerate(users)
  .find(user => 
    user.role === "admin" && 
    user.active
  );

Real-World Examples

Sum Values

const total = await enumerate(orders)
  .map(order => order.amount)
  .reduce((sum, amount) => sum + amount, 0);

Count by Category

const counts = await enumerate(items)
  .reduce((acc, item) => {
    acc[item.category] = (acc[item.category] || 0) + 1;
    return acc;
  }, {});

Find Maximum

const max = await enumerate(numbers)
  .reduce((max, n) => Math.max(max, n), -Infinity);

Build Index

const index = await enumerate(items)
  .reduce((acc, item) => {
    acc[item.id] = item;
    return acc;
  }, {});

Concatenate Strings

const combined = await enumerate(words)
  .reduce((acc, word) => acc + " " + word, "");

Collect Unique Values

const unique = await enumerate(items)
  .reduce((acc, item) => {
    acc.add(item);
    return acc;
  }, new Set());

Advanced Patterns

Running Average

const runningAvg = await enumerate(numbers)
  .reduce((acc, n) => {
    acc.sum += n;
    acc.count += 1;
    acc.average = acc.sum / acc.count;
    return acc;
  }, { sum: 0, count: 0, average: 0 });

Nested Grouping

const grouped = await enumerate(items)
  .reduce((acc, item) => {
    const cat = item.category;
    const type = item.type;
    
    acc[cat] = acc[cat] || {};
    acc[cat][type] = acc[cat][type] || [];
    acc[cat][type].push(item);
    
    return acc;
  }, {});

Frequency Map

const frequency = await enumerate(words)
  .reduce((acc, word) => {
    acc[word] = (acc[word] || 0) + 1;
    return acc;
  }, {});

// Find most common
const mostCommon = Object.entries(frequency)
  .sort((a, b) => b[1] - a[1])[0];

Accumulate with Transform

const processed = await enumerate(data)
  .reduce((acc, item) => {
    const transformed = transform(item);
    if (transformed.valid) {
      acc.push(transformed);
    }
    return acc;
  }, []);

Performance Tips

Use Specific Methods

// ❌ Slower
const count = await enumerate(items)
  .reduce((acc) => acc + 1, 0);

// ✅ Faster
const count = await enumerate(items).count();

Early Exit with some/every

// Stops at first match
const hasMatch = await enumerate(huge)
  .some(predicate);

// Better than
const matches = await enumerate(huge)
  .filter(predicate)
  .count();

Combine Operations

// ✅ One pass
const stats = await enumerate(numbers)
  .reduce((acc, n) => ({
    sum: acc.sum + n,
    count: acc.count + 1
  }), { sum: 0, count: 0 });

// ❌ Two passes
const sum = await enumerate(numbers).reduce((a, b) => a + b, 0);
const count = await enumerate(numbers).count();

Common Mistakes

Forgetting Initial Value

// ❌ Error with empty array
const sum = await enumerate([]).reduce((a, b) => a + b);

// ✅ Works with empty array
const sum = await enumerate([]).reduce((a, b) => a + b, 0);

Not Returning Accumulator

// ❌ Returns undefined
const result = await enumerate(items)
  .reduce((acc, item) => {
    acc.push(item);
    // Missing return!
  }, []);

// ✅ Returns accumulator
const result = await enumerate(items)
  .reduce((acc, item) => {
    acc.push(item);
    return acc;
  }, []);

Next Steps