Skip to main content

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)

MethodKeeps 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.

MethodReturns
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.
  • ReshapinggroupBy, pivotByGroup, join for when "interrogate" turns into "combine sources".
  • Cleaning datafill, filter for normalizing what queries return.