Skip to content

TakeUntil Method

The takeUntil() method returns items in the collection until the given callback returns true, or until it finds the given value. This method can be useful for getting a portion of an array until a specific condition is met.

Basic Syntax

typescript
// With callback function
collect(items).takeUntil((item: T) => boolean)

// With value
collect(items).takeUntil(value: T)

Examples

Basic Usage

typescript
import { collect } from 'ts-collect'

// Using a callback
const numbers = collect([1, 2, 3, 4, 5, 2, 1])
console.log(numbers.takeUntil(num => num > 3).all())
// [1, 2, 3]

// Using a value
const words = collect(['apple', 'banana', 'cherry', 'date'])
console.log(words.takeUntil('cherry').all())
// ['apple', 'banana']

Working with Objects

typescript
interface Task {
  id: number
  status: 'pending' | 'in_progress' | 'completed'
  priority: number
}

const tasks = collect<Task>([
  { id: 1, status: 'completed', priority: 1 },
  { id: 2, status: 'pending', priority: 2 },
  { id: 3, status: 'in_progress', priority: 3 },
  { id: 4, status: 'completed', priority: 4 }
])

// Take until we find a pending task
const completedTasks = tasks.takeUntil(task => task.status === 'pending')
console.log(completedTasks.all())
// [
//   { id: 1, status: 'completed', priority: 1 }
// ]

Real-world Examples

Log Analysis

typescript
interface LogEntry {
  timestamp: Date
  level: 'info' | 'warning' | 'error'
  message: string
  metadata?: Record<string, any>
}

class LogAnalyzer {
  private logs: Collection<LogEntry>

  constructor(logs: LogEntry[]) {
    this.logs = collect(logs)
  }

  getLogsUntilError(): LogEntry[] {
    return this.logs
      .takeUntil(log => log.level === 'error')
      .all()
  }

  getLogsUntilDate(date: Date): LogEntry[] {
    return this.logs
      .takeUntil(log => log.timestamp >= date)
      .all()
  }
}

// Usage
const analyzer = new LogAnalyzer([
  { timestamp: new Date('2024-01-01'), level: 'info', message: 'Started' },
  { timestamp: new Date('2024-01-02'), level: 'warning', message: 'Warning' },
  { timestamp: new Date('2024-01-03'), level: 'error', message: 'Error found' },
  { timestamp: new Date('2024-01-04'), level: 'info', message: 'Recovered' }
])

Transaction Processing

typescript
interface Transaction {
  id: string
  amount: number
  status: 'pending' | 'processed' | 'failed'
  date: Date
}

class TransactionProcessor {
  private transactions: Collection<Transaction>

  constructor(transactions: Transaction[]) {
    this.transactions = collect(transactions)
      .sortBy('date')
  }

  getValidTransactionsUntilFailure(): Transaction[] {
    return this.transactions
      .takeUntil(tx => tx.status === 'failed')
      .all()
  }

  getTransactionsUntilAmount(threshold: number): Transaction[] {
    let total = 0
    return this.transactions
      .takeUntil((tx) => {
        total += tx.amount
        return total > threshold
      })
      .all()
  }
}

Advanced Usage

Data Stream Processing

typescript
interface DataPoint {
  timestamp: Date
  value: number
  quality: number
}

class DataStreamProcessor {
  private dataPoints: Collection<DataPoint>
  private qualityThreshold: number

  constructor(data: DataPoint[], threshold: number = 0.8) {
    this.dataPoints = collect(data)
    this.qualityThreshold = threshold
  }

  getValidDataPoints(): DataPoint[] {
    return this.dataPoints
      .takeUntil(point => point.quality < this.qualityThreshold)
      .all()
  }

  getDataUntilAnomaly(tolerance: number): DataPoint[] {
    let previousValue: number | null = null
    return this.dataPoints
      .takeUntil((point) => {
        if (previousValue === null) {
          previousValue = point.value
          return false
        }
        const change = Math.abs(point.value - previousValue)
        previousValue = point.value
        return change > tolerance
      })
      .all()
  }
}

Document Parser

typescript
interface DocumentSection {
  id: string
  type: 'header' | 'content' | 'footer'
  text: string
}

class DocumentParser {
  private sections: Collection<DocumentSection>

  constructor(sections: DocumentSection[]) {
    this.sections = collect(sections)
  }

  getContentUntilFooter(): DocumentSection[] {
    return this.sections
      .takeUntil(section => section.type === 'footer')
      .all()
  }

  takeUntilEmptySection(): DocumentSection[] {
    return this.sections
      .takeUntil(section => section.text.trim() === '')
      .all()
  }
}

Type Safety

typescript
interface TypedItem {
  id: number
  value: string
  status: 'active' | 'inactive'
  metadata?: Record<string, any>
}

const items = collect<TypedItem>([
  { id: 1, value: 'one', status: 'active' },
  { id: 2, value: 'two', status: 'inactive' },
  { id: 3, value: 'three', status: 'active' }
])

// Type-safe callback
const activeItems = items.takeUntil((item: TypedItem) => {
  return item.status === 'inactive' // TypeScript validates status values
})

// Type-safe value match
const untilTwo = items.takeUntil({
  id: 2,
  value: 'two',
  status: 'inactive'
})

Return Value

  • Returns a new Collection containing items until the condition is met
  • Original collection remains unchanged
  • When using a callback:
    • Takes items until callback returns true
    • Excludes the item that matched the condition
  • When using a value:
    • Takes items until finding the matching value
    • Excludes the matching value
  • Maintains type safety with TypeScript

Released under the MIT License.