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

Transformations

Change data as it flows through your pipeline.

map()

Transform each item:

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

const doubled = await enumerate([1, 2, 3])
  .map(n => n * 2)
  .collect();
// [2, 4, 6]

With Async Functions

const results = await enumerate(urls)
  .map(async (url) => {
    const response = await fetch(url);
    return response.json();
  })
  .collect();

Type Transformations

const strings = await enumerate([1, 2, 3])
  .map(n => n.toString())
  .collect();
// ["1", "2", "3"]

Complex Transformations

const processed = await enumerate(rawData)
  .map(item => ({
    id: item.id,
    name: item.name.toUpperCase(),
    value: parseFloat(item.value),
    timestamp: new Date(item.timestamp)
  }))
  .collect();

flatMap()

Map and flatten in one step:

const words = await enumerate(["hello world", "foo bar"])
  .flatMap(line => line.split(" "))
  .collect();
// ["hello", "world", "foo", "bar"]

Expanding Items

const expanded = await enumerate([1, 2, 3])
  .flatMap(n => [n, n * 10])
  .collect();
// [1, 10, 2, 20, 3, 30]

Filtering While Mapping

const valid = await enumerate(data)
  .flatMap(item => {
    if (item.valid) {
      return [item.value];
    }
    return [];  // Skip invalid items
  })
  .collect();

filter()

Keep only matching items:

const evens = await enumerate([1, 2, 3, 4, 5])
  .filter(n => n % 2 === 0)
  .collect();
// [2, 4]

Complex Predicates

const active = await enumerate(users)
  .filter(user => 
    user.active && 
    user.lastLogin > cutoffDate &&
    user.role !== "guest"
  )
  .collect();

With Type Guards

const numbers = await enumerate(mixed)
  .filter((item): item is number => typeof item === "number")
  .collect();

transform()

Apply a TransformStream:

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

const decompressed = await read("file.gz")
  .transform(new DecompressionStream("gzip"))
  .lines
  .collect();

Custom Transform

const transformed = await enumerate(data)
  .transform(new TransformStream({
    transform(chunk, controller) {
      controller.enqueue(chunk.toUpperCase());
    }
  }))
  .collect();

Chaining Transformations

Combine multiple transformations:

const result = await enumerate(data)
  .map(item => item.trim())
  .filter(item => item.length > 0)
  .map(item => item.toUpperCase())
  .filter(item => item.startsWith("A"))
  .collect();

Real-World Examples

Parse CSV

const data = await read("data.csv")
  .lines
  .drop(1)  // Skip header
  .map(line => line.split(","))
  .map(([name, age, city]) => ({
    name,
    age: parseInt(age),
    city
  }))
  .filter(row => row.age >= 18)
  .collect();

Extract URLs

const urls = await read("page.html")
  .lines
  .flatMap(line => {
    const matches = line.match(/https?:\/\/[^\s"']+/g);
    return matches || [];
  })
  .collect();

Clean Data

const cleaned = await enumerate(rawData)
  .map(item => item.trim())
  .filter(item => item.length > 0)
  .map(item => item.toLowerCase())
  .filter(item => !item.startsWith("#"))
  .collect();

Transform JSON Lines

const objects = await read("data.jsonl")
  .lines
  .map(line => JSON.parse(line))
  .filter(obj => obj.status === "active")
  .map(obj => ({
    id: obj.id,
    name: obj.name,
    value: obj.value * 1.1  // Apply 10% increase
  }))
  .collect();

Performance Tips

Lazy Evaluation

Transformations don't run until you consume:

// Nothing happens yet
const pipeline = enumerate(data)
  .map(expensive)
  .filter(predicate);

// Now it runs
const result = await pipeline.collect();

Early Filtering

Filter before expensive operations:

// ✅ Filter first
const result = await enumerate(data)
  .filter(cheap)      // Fast filter
  .map(expensive)     // Expensive operation
  .collect();

// ❌ Map first
const result = await enumerate(data)
  .map(expensive)     // Runs on everything
  .filter(cheap)      // Then filters
  .collect();

Use take() to Limit

// Stop after 10 matches
const first10 = await enumerate(huge)
  .filter(predicate)
  .take(10)
  .collect();

Common Patterns

Normalize Data

const normalized = await enumerate(data)
  .map(item => ({
    ...item,
    name: item.name.trim().toLowerCase(),
    email: item.email.toLowerCase(),
    phone: item.phone.replace(/\D/g, "")
  }))
  .collect();

Extract Fields

const names = await enumerate(users)
  .map(user => user.name)
  .collect();

Conditional Transform

const processed = await enumerate(items)
  .map(item => {
    if (item.type === "A") {
      return processTypeA(item);
    } else {
      return processTypeB(item);
    }
  })
  .collect();

Batch Transform

const batched = await enumerate(items)
  .map((item, i) => ({
    ...item,
    batch: Math.floor(i / 100)
  }))
  .collect();

Error Handling

Errors in transformations propagate:

try {
  await enumerate(data)
    .map(item => {
      if (!item.valid) {
        throw new Error(`Invalid item: ${item.id}`);
      }
      return item.value;
    })
    .collect();
} catch (error) {
  console.error(`Transform failed: ${error.message}`);
}

Next Steps