pairwise

Each element paired with its successor: [a, b, c] becomes ((a, b), (b, c)).

Iterable<(A, A)> pairwise<A>(Iterable<A> iterable) FxAsyncIterable<(A, A)> pairwiseAsync<A>(FxAsyncIterable<A> iterable) Fx<(T, T)> Fx<T>.pairwise() // chain (sync) FxAsync<(T, T)> FxAsync<T>.pairwise() // chain (async) FxEvents<(T, T)> FxEvents<T>.pairwise() // chain (events)

Lecture

"How much did it change?" needs two elements at once — the previous and the current — and a plain map only ever sees one. The usual workarounds are an index loop (list[i - 1], off-by-one risk included) or zipping a list with itself shifted by one. pairwise() is that idea as an operator: it yields (previous, current) records, lazily, with n − 1 pairs for n elements. Fewer than two elements yield nothing — there is no pair to make.

The record fields keep both sides in reach: p.$2 - p.$1 is the delta, p.$2.compareTo(p.$1) the direction. It is exactly windowed(2) with typed records instead of two-element lists — reach for windowed when the neighborhood grows past two.

fxdart extension (no FxTS counterpart), after RxDart's pairwise. The async form computes nothing until pulled and composes with concurrent.

Demo 1 · Deltas between readings

Demo 2 · Direction of change

Try it yourself

Exercise: find the gaps in a sequence of timestamps.

Related: windowed — neighborhoods bigger than two · zip — pairing two different sequences · scan — carrying state instead of looking back one