Queries
Methods that interrogate a TimeSeries without changing it. None
of these mutate; none of them return a new TimeSeries (except the
subseries selectors at the bottom, which still leave the source
untouched). Reach for these when you need to ask the series for
something rather than transform it.
const cpu = TimeSeries.fromJSON({ name, schema, rows });
cpu.length; // 5
cpu.first()?.get('cpu'); // 0.31
cpu.timeRange(); // TimeRange { start, end }
cpu.tail('30s').reduce({ cpu: 'avg' }); // current state
Counts and bounds
length
Number of events. Plain property.
series.length; // number
timeRange()
Overall extent, undefined for an empty series.
series.timeRange();
// TimeRange | undefined
events
The underlying readonly array. Useful when you need an Array.map /
Array.find shape rather than the TimeSeries API:
series.events; // ReadonlyArray<EventForSchema<S>>
Single events
at(index)
Event at a positional index, or undefined if out of bounds.
Negative indices count from the end (-1 is the last event).
series.at(0); // first event, or undefined
series.at(-1); // last event, same as series.last()
first() / last()
series.first(); // EventForSchema<S> | undefined
series.last(); // EventForSchema<S> | undefined
find(predicate)
First event matching a predicate, like Array.prototype.find.
const spike = series.find((e) => (e.get('cpu') ?? 0) > 0.9);
Key-based lookups
For series with sorted keys (which is every TimeSeries), these are
binary-search lookups in O(log N). Pass a Time, Interval,
TimeRange, or raw timestamp.
includesKey(key)
Does any event match this key? true / false.
series.includesKey(new Time(0)); // boolean
series.includesKey(60_000); // also accepts a raw timestamp
bisect(key)
The index where this key would be inserted to keep order. Useful for "find the position of a timestamp without requiring an exact match":
const i = series.bisect(targetTimestamp);
// 0 ≤ i ≤ series.length
atOrBefore(key) / atOrAfter(key)
The nearest event that doesn't overshoot the key. Common pattern:
"the most recent reading at or before t":
const previous = series.atOrBefore(Date.now());
const next = series.atOrAfter(Date.now());
Predicates over the series
some(predicate) / every(predicate)
Array-style predicate scans:
series.some((e) => (e.get('cpu') ?? 0) > 0.9); // any spike?
series.every((e) => e.get('host') !== undefined); // all hosts known?
Subseries selection
These return a new TimeSeries<S> with the same schema and a
subset of events. The source is untouched.
slice(begin?, end?)
Positional, like Array.slice. Negative indices supported.
const head = series.slice(0, 100); // first 100 events
const tail = series.slice(-10); // last 10 events
tail(duration)
Trailing time window measured from the last event's begin().
"The last 30 seconds":
series.tail('30s'); // TimeSeries<S>
series.tail('5m').reduce({ cpu: 'avg' }); // current state
tail() with no argument is the identity.
before(boundary) / after(boundary)
Events strictly before / after a temporal boundary (timestamp or event key):
series.before(Date.now() - 60_000); // events older than 1m
series.after(start); // events newer than `start`
overlapping(range) / containedBy(range)
| Method | Keeps events… |
|---|---|
overlapping(range) | whose key intersects the range (any overlap) |
containedBy(range) | whose key is fully inside the range |
within(range) | same as containedBy (alias for ergonomics) |
const overlapping = series.overlapping(yesterdayRange);
const fully = series.containedBy(yesterdayRange);
trim(range)
Like overlapping(range), but partially-overlapping events get
their keys clipped to the range boundaries. Use this when you
want a subseries that "fits" inside a window without leaking past
either edge:
const clipped = series.trim(reportingWindow);
Range predicates
These return a boolean or a TimeRange, not a series.
overlaps(other)
Does this series's timeRange() overlap another?
seriesA.overlaps(seriesB); // boolean
seriesA.overlaps(rangeB); // also accepts a TimeRange directly
contains(other)
Is the other range / series fully inside this one's timeRange()?
day.contains(meeting); // boolean
intersection(other)
The shared TimeRange, or undefined if no overlap:
day.intersection(meeting); // TimeRange | undefined
Iteration
TimeSeries is iterable. for...of walks events in order; spread
yields the underlying array:
for (const event of series) {
console.log(event.begin(), event.get('cpu'));
}
const all = [...series]; // ReadonlyArray<EventForSchema<S>>
If you want array methods directly, reach for series.events:
const sum = series.events.reduce((s, e) => s + (e.get('cpu') ?? 0), 0);
Output forms
These are query-flavored too — they read the series and return data
in a different shape, without producing a new TimeSeries.
| Method | Returns |
|---|---|
toJSON({ rowFormat }) | JSON-friendly payload accepted by TimeSeries.fromJSON(...) |
toRows() | Array of normalized row tuples — same shape that new TimeSeries({ rows }) takes |
toObjects() | Array of objects keyed by schema column name |
toPoints() | Wide { ts, ...values }[] rows for charting |
toPoints is the chart-bridge — see
Charting. toJSON and friends are the
serialization story — see Creating series.
See also
- Transformations — once you've found the event(s) you want, the methods that change them.
- Reshaping —
groupBy,pivotByGroup,joinfor when "interrogate" turns into "combine sources". - Cleaning data —
fill,filterfor normalizing what queries return.