Skip to content

reduceAsync Method

The reduceAsync() method executes a reducer callback function asynchronously on each element of the collection, resulting in a single output value. This is particularly useful when the reduction operation requires asynchronous operations.

Basic Syntax

typescript
collect(items).reduceAsync<U>(
  callback: (acc: U, item: T) => Promise<U>,
  initialValue: U
): Promise<U>

Examples

Basic Usage

typescript
import { collect } from 'ts-collect'

// Simple async sum
const numbers = collect([1, 2, 3, 4])
const sum = await numbers.reduceAsync(async (acc, num) => {
  await new Promise(resolve => setTimeout(resolve, 100))
  return acc + num
}, 0)
console.log(sum)  // 10

// Concatenate strings with delay
const words = collect(['Hello', 'World'])
const sentence = await words.reduceAsync(async (acc, word) => {
  await new Promise(resolve => setTimeout(resolve, 100))
  return `${acc} ${word}`.trim()
}, '')

Working with Objects

typescript
interface CartItem {
  productId: string
  quantity: number
}

const cart = collect<CartItem>([
  { productId: 'P1', quantity: 2 },
  { productId: 'P2', quantity: 1 }
])

// Calculate total price with async price lookup
const total = await cart.reduceAsync(async (acc, item) => {
  const response = await fetch(`/api/products/${item.productId}/price`)
  const { price } = await response.json()
  return acc + (price * item.quantity)
}, 0)

Real-world Examples

Order Total Calculator

typescript
interface OrderItem {
  productId: string
  quantity: number
  customizations: string[]
}

interface PriceResult {
  subtotal: number
  tax: number
  total: number
}

class OrderCalculator {
  constructor(
    private priceApi: string,
    private taxRate: number
  ) {}

  async calculateTotal(items: Collection<OrderItem>): Promise<PriceResult> {
    const subtotal = await items.reduceAsync(async (acc, item) => {
      const [basePrice, customizationPrices] = await Promise.all([
        this.getProductPrice(item.productId),
        this.getCustomizationPrices(item.customizations)
      ])

      const itemTotal = (basePrice + customizationPrices) * item.quantity
      return acc + itemTotal
    }, 0)

    const tax = subtotal * this.taxRate
    const total = subtotal + tax

    return { subtotal, tax, total }
  }

  private async getProductPrice(productId: string): Promise<number> {
    const response = await fetch(`${this.priceApi}/products/${productId}`)
    const { price } = await response.json()
    return price
  }

  private async getCustomizationPrices(customizations: string[]): Promise<number> {
    if (customizations.length === 0) return 0

    const prices = await Promise.all(
      customizations.map(async (customId) => {
        const response = await fetch(`${this.priceApi}/customizations/${customId}`)
        const { price } = await response.json()
        return price
      })
    )

    return prices.reduce((sum, price) => sum + price, 0)
  }
}

Inventory Aggregator

typescript
interface StockItem {
  sku: string
  warehouse: string
}

interface StockSummary {
  available: number
  reserved: number
  incoming: number
  total: number
}

class InventoryAggregator {
  constructor(private inventoryApi: string) {}

  async aggregateStock(items: Collection<StockItem>): Promise<StockSummary> {
    return items.reduceAsync(async (summary, item) => {
      const stockInfo = await this.getStockInfo(item.sku, item.warehouse)

      return {
        available: summary.available + stockInfo.available,
        reserved: summary.reserved + stockInfo.reserved,
        incoming: summary.incoming + stockInfo.incoming,
        total: summary.total + stockInfo.total
      }
    }, {
      available: 0,
      reserved: 0,
      incoming: 0,
      total: 0
    })
  }

  private async getStockInfo(sku: string, warehouse: string): Promise<StockSummary> {
    const response = await fetch(
      `${this.inventoryApi}/stock/${sku}/warehouse/${warehouse}`
    )
    return response.json()
  }
}

Advanced Usage

Sales Performance Calculator

typescript
interface SalesRecord {
  orderId: string
  salesRepId: string
  products: Array<{
    sku: string
    quantity: number
  }>
  date: string
}

interface SalesMetrics {
  totalRevenue: number
  totalCost: number
  totalProfit: number
  productsSold: number
  uniqueCustomers: Set<string>
}

class SalesCalculator {
  constructor(
    private priceApi: string,
    private costApi: string
  ) {}

  async calculateMetrics(sales: Collection<SalesRecord>): Promise<SalesMetrics> {
    return sales.reduceAsync(async (metrics, sale) => {
      const [revenue, cost, customerInfo] = await Promise.all([
        this.calculateRevenue(sale.products),
        this.calculateCost(sale.products),
        this.getCustomerInfo(sale.orderId)
      ])

      return {
        totalRevenue: metrics.totalRevenue + revenue,
        totalCost: metrics.totalCost + cost,
        totalProfit: metrics.totalProfit + (revenue - cost),
        productsSold: metrics.productsSold + this.countProducts(sale.products),
        uniqueCustomers: metrics.uniqueCustomers.add(customerInfo.customerId)
      }
    }, {
      totalRevenue: 0,
      totalCost: 0,
      totalProfit: 0,
      productsSold: 0,
      uniqueCustomers: new Set<string>()
    })
  }

  private async calculateRevenue(products: Array<{ sku: string, quantity: number }>): Promise<number> {
    const revenues = await Promise.all(
      products.map(async ({ sku, quantity }) => {
        const response = await fetch(`${this.priceApi}/price/${sku}`)
        const { price } = await response.json()
        return price * quantity
      })
    )

    return revenues.reduce((sum, rev) => sum + rev, 0)
  }

  private async calculateCost(products: Array<{ sku: string, quantity: number }>): Promise<number> {
    const costs = await Promise.all(
      products.map(async ({ sku, quantity }) => {
        const response = await fetch(`${this.costApi}/cost/${sku}`)
        const { cost } = await response.json()
        return cost * quantity
      })
    )

    return costs.reduce((sum, cost) => sum + cost, 0)
  }

  private async getCustomerInfo(orderId: string) {
    const response = await fetch(`/api/orders/${orderId}/customer`)
    return response.json()
  }

  private countProducts(products: Array<{ quantity: number }>): number {
    return products.reduce((sum, { quantity }) => sum + quantity, 0)
  }
}

Type Safety

typescript
interface OrderItem {
  id: string
  quantity: number
}

interface OrderTotal {
  subtotal: number
  tax: number
  total: number
}

const items = collect<OrderItem>([
  { id: 'item1', quantity: 2 },
  { id: 'item2', quantity: 1 }
])

// Type-safe async reduction
const totals = await items.reduceAsync(async (acc: OrderTotal, item): Promise<OrderTotal> => {
  const response = await fetch(`/api/products/${item.id}/price`)
  const { price } = await response.json()
  const itemTotal = price * item.quantity

  return {
    subtotal: acc.subtotal + itemTotal,
    tax: acc.tax + (itemTotal * 0.1),
    total: acc.total + (itemTotal * 1.1)
  }
}, {
  subtotal: 0,
  tax: 0,
  total: 0
})

// TypeScript enforces return type Promise<OrderTotal>
const result: OrderTotal = await totals

Return Value

  • Returns a Promise that resolves to a single value
  • Value type matches initialValue type
  • Original collection remains unchanged
  • Maintains type safety with TypeScript
  • Processes items sequentially
  • Handles async/await operations

Common Use Cases

1. Price Calculations

  • Order totals
  • Cart summaries
  • Discount applications
  • Tax calculations
  • Shipping costs

2. Inventory Aggregation

  • Stock totals
  • Warehouse summaries
  • Location consolidation
  • Asset tracking
  • Availability checks

3. Sales Analytics

  • Revenue calculations
  • Profit summaries
  • Performance metrics
  • Commission tracking
  • Goal progress

4. Order Processing

  • Total calculations
  • Status aggregation
  • Fulfillment tracking
  • Shipping costs
  • Package details

5. Customer Metrics

  • Purchase history
  • Loyalty points
  • Activity summaries
  • Engagement metrics
  • Value calculation

6. Resource Management

  • Usage tracking
  • Cost accumulation
  • Time tracking
  • Capacity planning
  • Resource allocation

7. Financial Calculations

  • Transaction totals
  • Balance calculations
  • Fee accumulation
  • Currency conversion
  • Payment processing

8. Report Generation

  • Data aggregation
  • Summary calculations
  • Metric compilation
  • Statistical analysis
  • Performance summaries

9. Batch Processing

  • Data transformation
  • Status updates
  • Error tracking
  • Progress monitoring
  • Result accumulation

10. Integration Tasks

  • Data synchronization
  • Status consolidation
  • Error accumulation
  • Response processing
  • Result compilation

Released under the MIT License.