Skip to content

Macro Method

The macro() method allows you to add custom methods to the Collection class. This is particularly useful when you need to extend the functionality of collections with your own reusable methods.

Basic Syntax

typescript
Collection.macro(name: string, fn: Function)

Examples

Basic Usage

typescript
import { Collection } from 'ts-collect'

// Add a custom method to sum numbers and multiply by a factor
Collection.macro('sumAndMultiply', function (factor: number = 1) {
  return this.sum() * factor
})

// Use the custom method
const numbers = collect([1, 2, 3, 4])
console.log(numbers.sumAndMultiply(2)) // 20 (sum is 10, multiplied by 2)

Adding Type-Safe Methods

typescript
// Extend the Collection type
declare module 'ts-collect' {
  interface Collection<T> {
    uppercase(): Collection<string>
    titleCase(): Collection<string>
  }
}

// Add string transformation methods
Collection.macro('uppercase', function () {
  return this.map(item => String(item).toUpperCase())
})

Collection.macro('titleCase', function () {
  return this.map((item) => {
    return String(item)
      .toLowerCase()
      .split(' ')
      .map(word => word.charAt(0).toUpperCase() + word.slice(1))
      .join(' ')
  })
})

// Usage
const strings = collect(['hello world', 'typescript rocks'])
console.log(strings.uppercase().all())
// ['HELLO WORLD', 'TYPESCRIPT ROCKS']

console.log(strings.titleCase().all())
// ['Hello World', 'Typescript Rocks']

Real-world Examples

Custom Data Processing

typescript
interface Product {
  name: string
  price: number
  quantity: number
}

// Extend Collection type
declare module 'ts-collect' {
  interface Collection<T> {
    totalValue(): number
    inStock(): Collection<T>
    applyDiscount(percentage: number): Collection<T>
  }
}

// Add custom methods for product operations
Collection.macro('totalValue', function (this: Collection<Product>) {
  return this.sum(item => item.price * item.quantity)
})

Collection.macro('inStock', function (this: Collection<Product>) {
  return this.filter(item => item.quantity > 0)
})

Collection.macro('applyDiscount', function (this: Collection<Product>, percentage: number) {
  return this.map(item => ({
    ...item,
    price: item.price * (1 - percentage / 100)
  }))
})

// Usage
const products = collect<Product>([
  { name: 'Laptop', price: 1000, quantity: 5 },
  { name: 'Mouse', price: 50, quantity: 0 },
  { name: 'Keyboard', price: 100, quantity: 10 }
])

console.log(products.totalValue()) // 2000
console.log(products.inStock().all())
console.log(products.applyDiscount(10).all())

Data Analysis

typescript
interface DataPoint {
  value: number
  timestamp: string
}

// Extend Collection type
declare module 'ts-collect' {
  interface Collection<T> {
    average(): number
    standardDeviation(): number
    normalize(): Collection<T>
  }
}

// Add statistical methods
Collection.macro('average', function (this: Collection<DataPoint>) {
  return this.sum(item => item.value) / this.count()
})

Collection.macro('standardDeviation', function (this: Collection<DataPoint>) {
  const avg = this.average()
  const squareDiffs = this.map(item =>
    (item.value - avg) ** 2
  )
  return Math.sqrt(squareDiffs.sum() / this.count())
})

Collection.macro('normalize', function (this: Collection<DataPoint>) {
  const max = this.max(item => item.value)
  const min = this.min(item => item.value)

  return this.map(item => ({
    ...item,
    value: (item.value - min) / (max - min)
  }))
})

// Usage
const dataPoints = collect<DataPoint>([
  { value: 10, timestamp: '2024-01-01' },
  { value: 20, timestamp: '2024-01-02' },
  { value: 15, timestamp: '2024-01-03' }
])

console.log(dataPoints.average())
console.log(dataPoints.standardDeviation())
console.log(dataPoints.normalize().all())

Advanced Usage

Chain-aware Macros

typescript
declare module 'ts-collect' {
  interface Collection<T> {
    whenNotEmpty<R>(callback: (collection: Collection<T>) => R): R | Collection<T>
    transformIf(condition: boolean, callback: (item: T) => T): Collection<T>
  }
}

Collection.macro('whenNotEmpty', function (callback) {
  if (this.isNotEmpty()) {
    return callback(this)
  }
  return this
})

Collection.macro('transformIf', function (condition, callback) {
  return condition ? this.map(callback) : this
})

// Usage
const numbers = collect([1, 2, 3, 4, 5])

numbers
  .whenNotEmpty(collection => collection.filter(n => n > 2))
  .transformIf(true, n => n * 2)
  .all()

Type Safety

typescript
interface CustomItem {
  id: number
  value: string
}

// Extend Collection type with strictly typed methods
declare module 'ts-collect' {
  interface Collection<T> {
    customSort(key: keyof T): Collection<T>
    validateAll(predicate: (item: T) => boolean): boolean
  }
}

Collection.macro('customSort', function<T>(key: keyof T) {
  return this.sortBy(key)
})

Collection.macro('validateAll', function<T>(predicate: (item: T) => boolean) {
  return this.every(predicate)
})

// Usage with type safety
const items = collect<CustomItem>([
  { id: 2, value: 'b' },
  { id: 1, value: 'a' }
])

const sorted = items.customSort('id')
const valid = items.validateAll(item => item.id > 0)

Return Value

  • Each macro can return any value, but typically returns either:
    • The collection itself (for chainable methods)
    • A transformed collection
    • A computed value based on the collection's contents

Released under the MIT License.