Iterator protocol enhancements with functional programming methods for transforming, filtering, and consuming iterators efficiently.
Core Iterator functionality and protocol implementation for creating custom iterators.
/**
* Iterator constructor and protocol
*/
interface IteratorConstructor {
/**
* Creates a new Iterator
*/
new (): Iterator<any>;
/**
* Concatenates multiple iterables into a single iterator
* @param iterables - Iterables to concatenate
*/
concat<T>(...iterables: Iterable<T>[]): Iterator<T>;
}
interface Iterator<T, TReturn = any, TNext = undefined> {
/**
* Gets the next value from the iterator
* @param value - Optional value to pass to the iterator
*/
next(...args: [] | [TNext]): IteratorResult<T, TReturn>;
/**
* Optional return method for cleanup
* @param value - Return value
*/
return?(value?: TReturn): IteratorResult<T, TReturn>;
/**
* Optional throw method for error handling
* @param e - Error to throw
*/
throw?(e?: any): IteratorResult<T, TReturn>;
}
interface IteratorResult<T, TReturn = any> {
done: boolean;
value: T | TReturn;
}Usage Examples:
import Iterator from 'core-js-pure/stable/iterator';
// Custom iterator implementation
class RangeIterator extends Iterator {
constructor(start, end, step = 1) {
super();
this.current = start;
this.end = end;
this.step = step;
}
next() {
if (this.current < this.end) {
const value = this.current;
this.current += this.step;
return { value, done: false };
}
return { done: true };
}
}
// Using custom iterator
const range = new RangeIterator(0, 10, 2);
console.log([...range]); // [0, 2, 4, 6, 8]
// Generator function (built-in iterator)
function* fibonacci() {
let a = 0, b = 1;
while (true) {
yield a;
[a, b] = [b, a + b];
}
}
const fib = fibonacci();
console.log(fib.next().value); // 0
console.log(fib.next().value); // 1
console.log(fib.next().value); // 1
console.log(fib.next().value); // 2Methods for transforming iterator values through mapping and filtering operations.
interface Iterator<T> {
/**
* Creates iterator that applies mapping function to each value
* @param mapperFn - Function to transform each value
*/
map<U>(mapperFn: (value: T, index: number) => U): Iterator<U>;
/**
* Creates iterator that filters values based on predicate
* @param filtererFn - Function to test each value
*/
filter(filtererFn: (value: T, index: number) => boolean): Iterator<T>;
/**
* Creates iterator that maps values and flattens results
* @param mapperFn - Function that returns an iterable for each value
*/
flatMap<U>(mapperFn: (value: T, index: number) => Iterable<U>): Iterator<U>;
}Usage Examples:
import Iterator from 'core-js-pure/stable/iterator';
// Iterator transformations
function* numbers() {
for (let i = 1; i <= 10; i++) {
yield i;
}
}
// Map transformation
const doubled = numbers().map(x => x * 2);
console.log([...doubled]); // [2, 4, 6, 8, 10, 12, 14, 16, 18, 20]
// Filter transformation
const evens = numbers().filter(x => x % 2 === 0);
console.log([...evens]); // [2, 4, 6, 8, 10]
// Chain transformations
const processedNumbers = numbers()
.filter(x => x > 3)
.map(x => x * x)
.filter(x => x < 50);
console.log([...processedNumbers]); // [16, 25, 36, 49]
// FlatMap for nested iteration
function* words() {
yield 'hello';
yield 'world';
}
const chars = words().flatMap(word => [...word]);
console.log([...chars]); // ['h', 'e', 'l', 'l', 'o', 'w', 'o', 'r', 'l', 'd']
// Practical example: process file lines
function* fileLines() {
yield 'name,age,city';
yield 'Alice,25,NYC';
yield 'Bob,30,LA';
}
const csvData = fileLines()
.filter(line => !line.startsWith('name')) // Skip header
.map(line => line.split(','))
.map(([name, age, city]) => ({ name, age: parseInt(age), city }));
console.log([...csvData]);
// [{ name: 'Alice', age: 25, city: 'NYC' }, { name: 'Bob', age: 30, city: 'LA' }]Methods for reducing iterators to single values or testing conditions.
interface Iterator<T> {
/**
* Reduces iterator to single value using reducer function
* @param reducerFn - Function to combine values
* @param initialValue - Initial value for accumulator
*/
reduce<U>(reducerFn: (accumulator: U, currentValue: T, index: number) => U, initialValue: U): U;
reduce(reducerFn: (accumulator: T, currentValue: T, index: number) => T): T;
/**
* Tests whether all values satisfy the predicate
* @param predicate - Function to test each value
*/
every(predicate: (value: T, index: number) => boolean): boolean;
/**
* Tests whether any value satisfies the predicate
* @param predicate - Function to test each value
*/
some(predicate: (value: T, index: number) => boolean): boolean;
/**
* Finds first value that satisfies the predicate
* @param predicate - Function to test each value
*/
find(predicate: (value: T, index: number) => boolean): T | undefined;
}Usage Examples:
import Iterator from 'core-js-pure/stable/iterator';
function* numbers() {
for (let i = 1; i <= 5; i++) {
yield i;
}
}
// Reduce to single value
const sum = numbers().reduce((acc, val) => acc + val, 0);
console.log(sum); // 15
const product = numbers().reduce((acc, val) => acc * val);
console.log(product); // 120
// Testing conditions
const allPositive = numbers().every(x => x > 0);
console.log(allPositive); // true
const anyEven = numbers().some(x => x % 2 === 0);
console.log(anyEven); // true
const firstLarge = numbers().find(x => x > 3);
console.log(firstLarge); // 4
// Complex reduction example
function* transactions() {
yield { type: 'deposit', amount: 100 };
yield { type: 'withdrawal', amount: 30 };
yield { type: 'deposit', amount: 50 };
yield { type: 'withdrawal', amount: 20 };
}
const accountSummary = transactions().reduce(
(summary, transaction) => {
if (transaction.type === 'deposit') {
summary.balance += transaction.amount;
summary.deposits += transaction.amount;
} else {
summary.balance -= transaction.amount;
summary.withdrawals += transaction.amount;
}
summary.transactionCount++;
return summary;
},
{ balance: 0, deposits: 0, withdrawals: 0, transactionCount: 0 }
);
console.log(accountSummary);
// { balance: 100, deposits: 150, withdrawals: 50, transactionCount: 4 }Methods for controlling the number of values taken from iterators.
interface Iterator<T> {
/**
* Creates iterator that yields first N values
* @param limit - Number of values to take
*/
take(limit: number): Iterator<T>;
/**
* Creates iterator that skips first N values
* @param limit - Number of values to skip
*/
drop(limit: number): Iterator<T>;
}Usage Examples:
import Iterator from 'core-js-pure/stable/iterator';
function* infiniteNumbers() {
let i = 0;
while (true) {
yield i++;
}
}
// Take first N values
const firstTen = infiniteNumbers().take(10);
console.log([...firstTen]); // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
// Skip first N values
const skipFirst = infiniteNumbers().drop(5).take(5);
console.log([...skipFirst]); // [5, 6, 7, 8, 9]
// Pagination pattern
const paginate = (iterator, pageSize, pageNumber) => {
return iterator
.drop(pageSize * (pageNumber - 1))
.take(pageSize);
};
function* users() {
for (let i = 1; i <= 100; i++) {
yield { id: i, name: `User ${i}` };
}
}
// Get page 3 with 10 users per page
const page3 = paginate(users(), 10, 3);
console.log([...page3]); // Users 21-30
// Batch processing
const processBatches = (iterator, batchSize) => {
const batches = [];
let batch = [...iterator.take(batchSize)];
while (batch.length > 0) {
batches.push(batch);
batch = [...iterator.take(batchSize)];
}
return batches;
};Methods for consuming entire iterators and converting to other data structures.
interface Iterator<T> {
/**
* Converts iterator to array
*/
toArray(): T[];
/**
* Converts iterator to async iterator
*/
toAsync(): AsyncIterator<T>;
/**
* Executes function for each value in iterator
* @param callbackFn - Function to execute for each value
*/
forEach(callbackFn: (value: T, index: number) => void): void;
}Usage Examples:
import Iterator from 'core-js-pure/stable/iterator';
function* colors() {
yield 'red';
yield 'green';
yield 'blue';
}
// Convert to array
const colorArray = colors().toArray();
console.log(colorArray); // ['red', 'green', 'blue']
// Process each value
colors().forEach((color, index) => {
console.log(`Color ${index}: ${color}`);
});
// Color 0: red
// Color 1: green
// Color 2: blue
// Chaining with consumption
function* numbers() {
for (let i = 1; i <= 20; i++) {
yield i;
}
}
const processedArray = numbers()
.filter(x => x % 2 === 0)
.map(x => x * x)
.take(5)
.toArray();
console.log(processedArray); // [4, 16, 36, 64, 100]
// Side effects with forEach
const logAndCollect = (iterator) => {
const results = [];
iterator.forEach(value => {
console.log(`Processing: ${value}`);
results.push(value);
});
return results;
};Methods for proper cleanup and disposal of iterators.
interface Iterator<T> {
/**
* Disposes the iterator and releases resources
*/
dispose(): void;
}Usage Examples:
import Iterator from 'core-js-pure/stable/iterator';
// Iterator with cleanup
class FileLineIterator extends Iterator {
constructor(filename) {
super();
this.filename = filename;
this.fileHandle = null; // Simulated file handle
this.lines = ['line 1', 'line 2', 'line 3']; // Mock data
this.index = 0;
}
next() {
if (this.index < this.lines.length) {
return { value: this.lines[this.index++], done: false };
}
return { done: true };
}
dispose() {
if (this.fileHandle) {
console.log(`Closing file: ${this.filename}`);
this.fileHandle = null;
}
}
}
// Using with automatic disposal
const processFile = (filename) => {
const iterator = new FileLineIterator(filename);
try {
const lines = iterator
.filter(line => line.length > 0)
.map(line => line.trim())
.toArray();
return lines;
} finally {
iterator.dispose(); // Ensure cleanup
}
};
// Resource management pattern
const withIterator = (createIterator, processor) => {
const iterator = createIterator();
try {
return processor(iterator);
} finally {
if (iterator.dispose) {
iterator.dispose();
}
}
};
const result = withIterator(
() => new FileLineIterator('data.txt'),
(iterator) => iterator.map(line => line.toUpperCase()).toArray()
);Building efficient data processing pipelines with lazy evaluation.
import Iterator from 'core-js-pure/stable/iterator';
// Lazy CSV processor
class CSVProcessor {
constructor(data) {
this.data = data;
}
*[Symbol.iterator]() {
for (const line of this.data) {
yield line;
}
}
// Fluent interface for chaining
filter(predicate) {
return new CSVProcessor(
(function* (data) {
let index = 0;
for (const item of data) {
if (predicate(item, index++)) {
yield item;
}
}
})(this)
);
}
map(mapper) {
return new CSVProcessor(
(function* (data) {
let index = 0;
for (const item of data) {
yield mapper(item, index++);
}
})(this)
);
}
take(count) {
return new CSVProcessor(
(function* (data) {
let taken = 0;
for (const item of data) {
if (taken >= count) break;
yield item;
taken++;
}
})(this)
);
}
toArray() {
return [...this];
}
}
// Usage
const csvData = [
'name,age,city',
'Alice,25,NYC',
'Bob,30,LA',
'Charlie,35,Chicago',
'Diana,28,Boston'
];
const result = new CSVProcessor(csvData)
.filter(line => !line.startsWith('name')) // Skip header
.map(line => {
const [name, age, city] = line.split(',');
return { name, age: parseInt(age), city };
})
.filter(person => person.age > 27)
.take(2)
.toArray();
console.log(result);
// [{ name: 'Bob', age: 30, city: 'LA' }, { name: 'Charlie', age: 35, city: 'Chicago' }]Creating and working with infinite sequences using iterators.
import Iterator from 'core-js-pure/stable/iterator';
// Mathematical sequences
const sequences = {
*fibonacci() {
let [a, b] = [0, 1];
while (true) {
yield a;
[a, b] = [b, a + b];
}
},
*primes() {
yield 2;
const primes = [2];
let candidate = 3;
while (true) {
const isPrime = !primes.some(p =>
p * p <= candidate && candidate % p === 0
);
if (isPrime) {
primes.push(candidate);
yield candidate;
}
candidate += 2;
}
},
*collatz(n) {
while (n !== 1) {
yield n;
n = n % 2 === 0 ? n / 2 : 3 * n + 1;
}
yield 1;
}
};
// Use infinite sequences
const firstTenFib = [...sequences.fibonacci().take(10)];
console.log(firstTenFib); // [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
const primesUnder100 = [...sequences.primes().filter(p => p < 100)];
console.log(primesUnder100);
const collatzSequence = [...sequences.collatz(7)];
console.log(collatzSequence); // [7, 22, 11, 34, 17, 52, 26, 13, 40, 20, 10, 5, 16, 8, 4, 2, 1]Processing event streams using iterator helpers.
import Iterator from 'core-js-pure/stable/iterator';
// Mock event stream
class EventStream extends Iterator {
constructor() {
super();
this.events = [
{ type: 'click', x: 100, y: 200, timestamp: 1000 },
{ type: 'mousemove', x: 105, y: 205, timestamp: 1100 },
{ type: 'click', x: 110, y: 210, timestamp: 1200 },
{ type: 'keypress', key: 'a', timestamp: 1300 },
{ type: 'click', x: 115, y: 215, timestamp: 1400 }
];
this.index = 0;
}
next() {
if (this.index < this.events.length) {
return { value: this.events[this.index++], done: false };
}
return { done: true };
}
}
// Event processing pipeline
const processEvents = (eventStream) => {
return eventStream
.filter(event => event.type === 'click')
.map(event => ({
...event,
position: `${event.x},${event.y}`,
relativeTime: event.timestamp - 1000
}))
.filter(event => event.relativeTime < 300);
};
const clicks = processEvents(new EventStream()).toArray();
console.log(clicks);
// Filtered and transformed click events within time window