Skip to content

A powerful, yet lightweight, Laravel-like Collections written for TypeScript.

License

Notifications You must be signed in to change notification settings

stacksjs/ts-collect

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

65 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Social Card of this repo

npm version GitHub Actions Commitizen friendly

ts-collect

A powerful, fully-typed collections library for TypeScript, combining Laravel's collection elegance with advanced data processing capabilities. Features lazy evaluation, statistical analysis, machine learning operations, and comprehensive data manipulation toolsβ€”all with zero dependencies.

Features

  • Lightweight & Dependency-free
  • Fully typed
  • Laravel-inspired APIs

Core Operations (Laravel Collection API)

  • Standard operations (map, filter, reduce)
  • FlatMap and MapSpread operations
  • Element access (first, firstOrFail, last, nth)
  • Subset operations (take, skip, slice)
  • Unique value handling
  • Chunk operations
  • Tap and Pipe utilities
  • Collection conversion (toArray, toMap, toSet)
  • Collection inspection (count, isEmpty, isNotEmpty)
  • Combine and collapse operations
  • Contains checks (contains, containsOneItem)
  • Each iterations (each, eachSpread)
  • Only and except operations
  • Forget and random selection
  • Push, prepend, and put operations
  • Skip and take variants (skipUntil, skipWhile, takeUntil, takeWhile)
  • Sole item retrieval
  • Conditional execution (when, unless)
  • Wrap and unwrap operations

Advanced Array & Object Operations

  • GroupBy with multiple key support
  • Value extraction (pluck)
  • Where clause variations
    • Basic where operations (where, whereIn, whereNotIn)
    • Range checks (whereBetween, whereNotBetween)
    • Null handling (whereNull, whereNotNull)
    • Pattern matching (whereLike, whereRegex)
    • Type checks (whereInstanceOf)
  • Comprehensive sorting
    • Basic sort operations
    • Key-based sorting (sortBy, sortByDesc)
    • Key sorting (sortKeys, sortKeysDesc)
  • Pagination & Cursor iteration
  • Data partitioning
  • Set operations (union, intersect, diff, symmetricDiff)
  • Advanced products (cartesianProduct)
  • Recursive operations (mergeRecursive, replaceRecursive)

Advanced Transformations

  • Group transformations (mapToGroups)
  • Array handling (mapSpread, mapWithKeys)
  • Conditional mapping (mapUntil, mapOption)
  • Data restructuring (transform)
  • Type system integration (cast, mapInto)
  • Property operations (pick, omit)
  • Fuzzy matching algorithms
  • Key-value transformations (flip, undot)

Statistical Operations

  • Basic statistics
    • Sum and averages
    • Median and mode
    • Range (min, max)
    • Products
  • Advanced statistics
    • Standard deviation
    • Variance analysis
    • Percentile calculations
    • Correlation coefficients
    • Entropy measures
    • Z-score computations
    • Distribution analysis (kurtosis, skewness)
    • Covariance calculations

Time Series Analysis

  • Series conversion and formatting
  • Moving average calculations
  • Trend detection and analysis
  • Seasonality identification
  • Time-based forecasting
  • Temporal grouping operations
  • Time-based aggregations
  • Interval handling

Machine Learning Operations

  • Clustering algorithms
    • K-means implementation
    • Cluster analysis tools
  • Regression analysis
    • Linear regression
    • Multi-variable regression
  • Classification tools
    • K-nearest neighbors (KNN)
    • Naive Bayes classifier
  • Anomaly detection systems
  • Data preparation
    • Normalization
    • Outlier handling
    • Feature scaling

Async & Performance Optimization

  • Asynchronous operations
    • Async mapping
    • Async filtering
    • Async reduction
  • Parallel processing capabilities
  • Batch processing systems
  • Lazy evaluation strategies
  • Caching mechanisms
  • Performance tools
    • Profiling utilities
    • Memory optimization
    • Index management
    • Operation monitoring

Data Validation & Quality

  • Validation framework
    • Schema validation
    • Custom rules
    • Async validation
  • Data sanitization tools
  • Quality metrics
  • Constraint management
  • Error handling
  • Type enforcement

Text Processing

  • String manipulation
    • Join operations
    • Implode functionality
    • Case transformation
  • URL slug generation
  • Text analysis
    • Word frequency
    • N-gram generation
    • Sentiment analysis
  • Pattern matching
  • String normalization

Serialization & Export

  • Multiple format support
    • JSON serialization
    • CSV generation
    • XML export
  • Query generation
    • SQL queries
    • GraphQL operations
  • Integration formats
    • Elasticsearch bulk
    • Pandas DataFrame
  • Custom formatting options

Streaming & I/O

  • Stream operations
    • Stream creation
    • Stream consumption
  • Batch streaming
  • Memory-efficient processing
  • Buffered operations

Advanced Mathematical Operations

  • Signal processing
    • Fast Fourier Transform (FFT)
    • Signal interpolation
    • Convolution operations
  • Calculus operations
    • Differentiation
    • Integration
  • Numerical methods
  • Mathematical optimizations

Special Data Types Support

  • Geographic calculations
    • Distance computations
    • Coordinate handling
  • Financial operations
    • Money formatting
    • Currency handling
  • DateTime operations
    • Formatting
    • Timezone handling
  • Complex number support
    • Basic operations
    • Advanced computations

Versioning & History

  • Version management
    • Version tracking
    • History storage
  • Change tracking
    • Diff generation
    • Change detection
  • History operations
    • Rollback support
    • Version comparison

Development Tools

  • Development aids
    • Playground environment
    • Debugging utilities
  • Analysis tools
    • Pipeline visualization
    • Performance benchmarking
  • Development modes
    • Debug mode
    • Strict mode

Utility Features

  • System configuration
    • Configuration management
    • Environment handling
  • Internationalization
    • Locale support
    • Timezone management
  • Error handling
    • Error modes
    • Exception handling
  • Resource management
    • Memory tracking
    • Resource cleanup

Get Started

bun install ts-collect

Usage

Basic Collection Operations

import { collect } from 'ts-collect'

// Create a collection
const collection = collect([1, 2, 3, 4, 5])

// Basic operations with chaining
const result = collection
  .map(n => n * 2) // [2, 4, 6, 8, 10]
  .filter(n => n > 5) // [6, 8, 10]
  .take(2) // [6, 8]
  .toArray()

// Unique values with custom key
const users = collect([
  { id: 1, role: 'admin' },
  { id: 2, role: 'user' },
  { id: 3, role: 'admin' }
])
const uniqueRoles = users.unique('role') // [{ id: 1, role: 'admin' }, { id: 2, role: 'user' }]

// Chunk data into smaller arrays
const chunks = collection.chunk(2) // [[1, 2], [3, 4], [5]]

// Find elements
const first = collection.first() // 1
const last = collection.last() // 5
const secondItem = collection.nth(1) // 2

// all() - Get all items as array
const items = collection.all() // [1, 2, 3, 4, 5]

// average/avg - Calculate average of items
collection.average() // 3
collection.avg() // 3

// chunk - Split collection into smaller collections
collection.chunk(2) // [[1, 2], [3, 4], [5]]

// collapse - Flatten a collection of arrays
const nested = collect([[1, 2], [3, 4], [5]])
nested.collapse() // [1, 2, 3, 4, 5]

// combine - Create collection by combining arrays
const keys = collect(['name', 'age'])
const values = ['John', 25]
keys.combine(values) // { name: 'John', age: 25 }

// contains/containsOneItem - Check for item existence
collection.contains(3) // true
collection.containsOneItem() // false

// countBy - Count occurrences by value
const items = collect(['apple', 'banana', 'apple', 'orange'])
items.countBy() // Map { 'apple' => 2, 'banana' => 1, 'orange' => 1 }

// diff/diffAssoc/diffKeys - Find differences between collections
const col1 = collect([1, 2, 3])
const col2 = collect([2, 3, 4])
col1.diff(col2) // [1]

// dd/dump - Dump collection and die or just dump
collection.dump() // Console logs items
collection.dd() // Console logs and exits

// each/eachSpread - Iterate over items
collection.each(item => console.log(item))
collection.eachSpread((a, b) => console.log(a, b)) // For array items

// except/only - Get all items except/only specified keys
const user = collect({ id: 1, name: 'John', age: 25 })
user.except('age') // { id: 1, name: 'John' }
user.only('name', 'age') // { name: 'John', age: 25 }

// firstOrFail - Get first item or throw
collection.firstOrFail() // 1 or throws if empty

// firstWhere - Get first item matching criteria
const users = collect([
  { id: 1, name: 'John' },
  { id: 2, name: 'Jane' }
])
users.firstWhere('name', 'Jane') // { id: 2, name: 'Jane' }

// flip - Swap keys and values
const flipped = collect({ name: 'John' }).flip() // { John: 'name' }

// forget - Remove an item by key
const array = collect(['a', 'b', 'c'])
array.forget(1) // ['a', 'c']

// has/get - Check key existence / Get value
const item = collect({ name: 'John' })
item.has('name') // true
item.get('name') // 'John'

// mapInto - Map items into new class instances
class User {
  name: string = ''
  greet() { return `Hello ${this.name}` }
}
collect([{ name: 'John' }])
  .mapInto(User)
  .first()
  .greet() // "Hello John"

// prepend/push/put - Add items
collection.prepend(0) // [0, 1, 2, 3, 4, 5]
collection.push(6) // [1, 2, 3, 4, 5, 6]
collection.put('key', 'value') // Adds/updates key-value

// random - Get random item(s)
collection.random() // Random item
collection.random(2) // Array of 2 random items

// skip/skipUntil/skipWhile - Skip items
collection.skip(2) // [3, 4, 5]
collection.skipUntil(3) // [3, 4, 5]
collection.skipWhile(n => n < 3) // [3, 4, 5]

// sole - Get only item in single-item collection
collect([1]).sole() // 1 (throws if not exactly one item)

// take/takeUntil/takeWhile - Take items
collection.take(2) // [1, 2]
collection.takeUntil(3) // [1, 2]
collection.takeWhile(n => n < 3) // [1, 2]

// when/unless - Conditional execution
collection
  .when(true, col => col.take(3))
  .unless(false, col => col.take(2))

// wrap/unwrap - Wrap/unwrap value in collection
collect().wrap([1, 2, 3]) // Collection([1, 2, 3])
collection.unwrap() // [1, 2, 3]

Working with Objects

interface User {
  id: number
  name: string
  role: string
}

const users: User[] = [
  { id: 1, name: 'John', role: 'admin' },
  { id: 2, name: 'Jane', role: 'user' },
  { id: 3, name: 'Bob', role: 'user' }
]

const collection = collect(users)

// Group by a key
const byRole = collection.groupBy('role')
// Map { 'admin' => [{ id: 1, ... }], 'user' => [{ id: 2, ... }, { id: 3, ... }] }

// Pluck specific values
const names = collection.pluck('name')
// ['John', 'Jane', 'Bob']

// Find where
const admins = collection.where('role', 'admin')
// [{ id: 1, name: 'John', role: 'admin' }]

Advanced Array & Object Operations

interface User {
  id: number
  name: string
  role: string
  department: string
  salary: number
  joinedAt: Date
}

const users: User[] = [
  {
    id: 1,
    name: 'John',
    role: 'admin',
    department: 'IT',
    salary: 80000,
    joinedAt: new Date('2023-01-15')
  },
  {
    id: 2,
    name: 'Jane',
    role: 'manager',
    department: 'Sales',
    salary: 90000,
    joinedAt: new Date('2023-03-20')
  },
  {
    id: 3,
    name: 'Bob',
    role: 'developer',
    department: 'IT',
    salary: 75000,
    joinedAt: new Date('2023-06-10')
  }
]

const collection = collect(users)

// Complex grouping by multiple fields
const groupedUsers = collection.groupByMultiple('department', 'role')
// Map {
//   'IT::admin' => [{ id: 1, ... }],
//   'Sales::manager' => [{ id: 2, ... }],
//   'IT::developer' => [{ id: 3, ... }]
// }

// Advanced filtering combinations
const seniorITStaff = collection
  .where('department', 'IT')
  .filter((user) => {
    const monthsEmployed = (new Date().getTime() - user.joinedAt.getTime()) / (1000 * 60 * 60 * 24 * 30)
    return monthsEmployed > 6
  })
  .whereBetween('salary', 70000, 85000)
  .toArray()

// Sort by multiple fields
const sorted = collection
  .sortBy('department')
  .sortBy('salary', 'desc')
  .toArray()

// Transform data structure
const transformed = collection.transform<{ fullName: string, info: string }>({
  fullName: user => user.name,
  info: user => `${user.role} in ${user.department}`
})

// Pagination
const page = collection.paginate(2, 1) // 2 items per page, first page
// {
//   data: [...],
//   total: 3,
//   perPage: 2,
//   currentPage: 1,
//   lastPage: 2,
//   hasMorePages: true
// }

Advanced Filtering & Pattern Matching

interface Product {
  id: number
  name: string
  description: string
  price: number
  categories: string[]
  inStock: boolean
}

const products = collect<Product>([
  {
    id: 1,
    name: 'Premium Laptop',
    description: 'High-performance laptop with 16GB RAM',
    price: 1299.99,
    categories: ['electronics', 'computers'],
    inStock: true
  },
  // ... more products
])

// Fuzzy search
const searchResults = products.fuzzyMatch('name', 'laptop', 0.8)

// Regular expression matching
const matched = products.whereRegex('description', /\d+GB/)

// Complex conditional filtering
const filtered = products
  .when(true, collection =>
    collection.filter(p => p.price > 1000))
  .unless(false, collection =>
    collection.filter(p => p.inStock))

// Pattern matching with whereLike
const pattern = products.whereLike('name', '%Laptop%')

Statistical Operations

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

numbers.sum() // 21
numbers.avg() // 3.5
numbers.median() // 3.5
numbers.min() // 1
numbers.max() // 6
numbers.standardDeviation() // { population: 1.707825127659933, sample: 1.8708286933869707 }

Time Series Data

const timeData = [
  { date: '2024-01-01', value: 100 },
  { date: '2024-01-02', value: 150 },
  { date: '2024-01-03', value: 120 }
]

const series = collect(timeData).timeSeries({
  dateField: 'date',
  valueField: 'value',
  interval: 'day'
})

// Calculate moving average
const movingAvg = series.movingAverage({ window: 2 })

Lazy Evaluation

const huge = collect(Array.from({ length: 1000000 }, (_, i) => i))

// Operations are deferred until needed
const result = huge
  .lazy()
  .filter(n => n % 2 === 0)
  .map(n => n * 2)
  .take(5)
  .toArray()

Async Operations & Batch Processing

// Process large datasets in batches
const largeDataset = collect(Array.from({ length: 10000 }, (_, i) => ({
  id: i,
  data: `Data ${i}`
})))

// Parallel processing with batches
await largeDataset.parallel(
  async (batch) => {
    const processed = await processItems(batch)
    return processed
  },
  { chunks: 4, maxConcurrency: 2 }
)

// Async mapping
const asyncMapped = await largeDataset
  .mapAsync(async (item) => {
    const result = await fetchDataForItem(item)
    return { ...item, ...result }
  })

// Batch processing with cursor
for await (const batch of largeDataset.cursor(100)) {
  await processBatch(batch)
}

Data Validation & Sanitization

interface UserData {
  email: string
  age: number
  username: string
}

const userData = collect<UserData>([
  { email: '[email protected]', age: 25, username: 'john_doe' },
  { email: 'invalid-email', age: -5, username: 'admin' }
])

// Validate data
const validationResult = await userData.validate({
  email: [
    email => /^[^@]+@[^@][^.@]*\.[^@]+$/.test(email),
    email => email.length <= 255
  ],
  age: [
    age => age >= 0,
    age => age <= 120
  ],
  username: [
    username => username.length >= 3,
    username => /^\w+$/.test(username)
  ]
})

// Sanitize data
const sanitized = userData.sanitize({
  email: email => email.toLowerCase().trim(),
  age: age => Math.max(0, Math.min(120, age)),
  username: username => username.toLowerCase().replace(/\W/g, '')
})

Data Analysis & Statistics

interface SalesData {
  product: string
  revenue: number
  cost: number
  date: string
  region: string
}

const sales: SalesData[] = [
  { product: 'A', revenue: 100, cost: 50, date: '2024-01-01', region: 'North' },
  { product: 'B', revenue: 200, cost: 80, date: '2024-01-01', region: 'South' },
  { product: 'A', revenue: 150, cost: 60, date: '2024-01-02', region: 'North' },
  { product: 'B', revenue: 180, cost: 75, date: '2024-01-02', region: 'South' },
]

const salesCollection = collect(sales)

// Advanced statistical analysis
const stats = salesCollection
  .describe('revenue') // Get statistical summary
  .pluck('revenue')
  .pipe(numbers => ({
    sum: numbers.sum(),
    average: numbers.avg(),
    median: numbers.median(),
    stdDev: numbers.standardDeviation(),
    variance: numbers.variance()
  }))

// Pivot table analysis
const pivotData = salesCollection.pivotTable(
  'product', // rows
  'region', // columns
  'revenue', // values
  'sum' // aggregation method
)

// Time series analysis with moving averages
const timeSeries = salesCollection
  .timeSeries({
    dateField: 'date',
    valueField: 'revenue',
    interval: 'day'
  })
  .movingAverage({ window: 2, centered: true })

// Correlation analysis
const correlation = salesCollection.correlate('revenue', 'cost')

// Detect anomalies in revenue
const anomalies = salesCollection.detectAnomalies({
  method: 'zscore',
  threshold: 2,
  features: ['revenue']
})

Performance Optimization

// Cache expensive operations
const cached = collection
  .map(expensiveOperation)
  .cache(60000) // Cache for 60 seconds

// Lazy evaluation for large datasets
const lazy = collection
  .lazy()
  .filter(predicate)
  .map(transform)
  .take(10)

// Optimize queries with indexing
const indexed = collection
  .index(['id', 'category'])
  .where('category', 'electronics')
  .where('id', 123)

// Profile performance
const metrics = await collection.profile()
// { time: 123, memory: 456 }

// Instrumentation
collection
  .instrument(stats => console.log('Operation stats:', stats))
  .map(transform)
  .filter(predicate)

Advanced Serialization

// Export to different formats
const json = collection.toJSON({ pretty: true })
const csv = collection.toCsv()
const xml = collection.toXml()

// SQL generation
const sql = collection.toSQL('users')

// GraphQL query generation
const graphql = collection.toGraphQL('User')

// Elasticsearch bulk format
const elastic = collection.toElastic('users')

// Pandas DataFrame generation
const pandas = collection.toPandas()

Type Safety

interface Product {
  id: number
  name: string
  price: number
}

// Collection is fully typed
const products = collect<Product>([
  { id: 1, name: 'Widget', price: 9.99 }
])

// TypeScript will catch errors
products.where('invalid', 'value') // Type error!

For more detailed documentation and examples, please visit our documentation site.

Testing

bun test

Changelog

Please see our releases page for more information on what has changed recently.

Contributing

Please see CONTRIBUTING for details.

Community

For help, discussion about best practices, or any other conversation that would benefit from being searchable:

Discussions on GitHub

For casual chit-chat with others using this package:

Join the Stacks Discord Server

Postcardware

Stacks OSS will always stay open-sourced, and we will always love to receive postcards from wherever Stacks is used! And we also publish them on our website. Thank you, Spatie.

Our address: Stacks.js, 12665 Village Ln #2306, Playa Vista, CA 90094, United States 🌎

Sponsors

We would like to extend our thanks to the following sponsors for funding Stacks development. If you are interested in becoming a sponsor, please reach out to us.

πŸ“„ License

The MIT License (MIT). Please see LICENSE for more information.

Made with πŸ’™