attach

Pair each value with what you derive from it — the input stays beside its result.

Iterable<(A, B)> attach<A, B>(B Function(A a) f, Iterable<A> iterable) FxAsyncIterable<(A, B)> attachAsync<A, B>(FutureOr<B> Function(A a) f, FxAsyncIterable<A> iterable) Fx<(T, R)> Fx<T>.attach<R>(R Function(T a) f) // chain (sync) FxAsync<(T, R)> FxAsync<T>.attach<R>(FutureOr<R> Function(T a) f) // chain (async)

Lecture

map replaces each value with its result — and the moment you need the input again downstream (to fall back, to label, to log), you find yourself hand-building records: .map((x) async => (x, await f(x))). attach(f) is that idiom as an operator: it yields (value, f(value)) pairs, lazily.

It earns its keep in async chains. Look up a price per item and the pair keeps the item next to the (maybe missing) price, so the fallback r.$2 ?? r.$1.listPrice and the "which SKU was that?" label are both still in reach. The async form is built on mapAsync, so it is parallel-safe — put concurrent(n) after it and n lookups run at once.

Dart-native addition (no FxTS counterpart). When you only need the derived value, keep using map; when you need it keyed by the input as a lookup table, that is indexBy.

Demo 1 · The input survives the map

Demo 2 · Async lookups with fallback

Try it yourself

Exercise: keep each query beside its search results.

Related: map — when the input can go · zip — pairing two separate sequences · concurrent — bound the async fan-out after attaching