UNPKG

@everwhen/temporal

Version:
156 lines (155 loc) 4.7 kB
import { Duration, } from "./duration.js"; import { invariant } from "./fn/misc.js"; import { isPlainTime, isPlainYearMonth } from "./is.js"; export class Sequence { start; end; step; #duration; constructor(start, end, step) { // Excluding PlainTime as time is circular, it is simultaneously before and after midnight. if (!isPlainTime(start)) { invariant(start.compare(end) <= 0, `Invalid bounds. start: ${start.toString()} - end: ${end.toString()} `); } this.start = start; this.end = end; this.step = step; this.#duration = this.start.until(this.end); } static from(bounds) { let stepDefault; if (isPlainTime(bounds.start)) { stepDefault = { hours: 1 }; } else if (isPlainYearMonth(bounds.start)) { stepDefault = { months: 1 }; } else { stepDefault = { days: 1 }; } return new Sequence(bounds.start, bounds.end, bounds.step ?? stepDefault); } *[Symbol.iterator]() { let current = this.start; // Special handling for PlainTime as time is circular, incrementing `current` will eventually circle back to 00:00, causing a infinite loop let timeAccumulated = Duration.from({ seconds: 0 }); yield current; while (current.compare(this.end) < 0) { if (isPlainTime(this.start)) { timeAccumulated = timeAccumulated.add(this.step); if (timeAccumulated.compare(this.#duration) > 0) { break; } } current = current.add(this.step); yield current; } } get bounds() { return { start: this.start, end: this.end }; } *items() { let timeAccumulated = Duration.from({ seconds: 0 }); const item = { value: this.start, next: this.start.add(this.step), }; yield item; while (item.value.compare(this.end) < 0) { if (isPlainTime(this.start)) { timeAccumulated = timeAccumulated.add(this.step); if (timeAccumulated.compare(this.#duration) > 0) { break; } } item.previous = item.value; item.value = item.value.add(this.step); const next = item.value.add(this.step); if (next.compare(this.end) <= 0) { item.next = next; } else { delete item.next; } yield item; } } forEach(callbackfn) { let index = 0; for (const val of this) { callbackfn(val, index); index += 1; } } map(mapper) { const items = []; for (const tem of this) { items.push(mapper(tem)); } return items; } group(keyFn) { const groups = new Map(); for (const item of this) { const key = keyFn(item); if (!groups.has(key)) { groups.set(key, []); } groups.get(key).push(item); } return groups; } select(predicate, mapper) { const values = []; for (const item of this) { if (predicate(item)) { values.push(mapper(item)); } } return values; } filter(predicate) { return this.select(predicate, (t) => t); } with(bounds) { return new Sequence(bounds.start ?? this.start, bounds.end ?? this.end, bounds.step ?? this.step); } get length() { return Array.from(this).length; } contains(target) { return this.start.compare(target) <= 0 && this.end.compare(target) >= 0; } toMap(mapper) { const result = new Map(); for (const item of this) { const key = item.toString(); const value = mapper(item); result.set(key, value); } return result; } toJSON() { return { start: this.start.toString(), end: this.end.toString(), step: Duration.from(this.step).toString(), }; } indexOf(target) { if (!this.contains(target)) { return -1; } let index = 0; for (const point of this) { if (point.equals(target)) { return index; } index++; } return index; } toString() { return `[${this.start.toString()}, ${this.end.toString()}, ${Duration.from(this.step).toString()}]`; } }