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.