expand

Transforma cada elemento en un iterable y luego aplana los resultados un nivel — el mismo contrato que Iterable.expand.

Iterable<B> expand<A, B>(Iterable<B> Function(A a) f, Iterable<A> iterable) FxAsyncIterable<B> expandAsync<A, B>(FutureOr<Iterable<B>> Function(A a) f, FxAsyncIterable<A> iterable) Fx<R> Fx.expand<R>(Iterable<R> Function(T a) f) // chain FxAsync<R> FxAsync.expand<R>(FutureOr<Iterable<R>> Function(T a) f) Iterable<B> flatMap<A, B>(...) // FxTS alias

Lección

expand es map seguido de un nivel de aplanado: cada elemento se convierte en una colección de resultados, y esas colecciones se empalman en una única secuencia perezosa y plana. Es la herramienta para «una entrada, muchas salidas»: partir una frase en palabras, expandir un usuario en sus pedidos, convertir un rango en pares. expand es el nombre idiomático en Dart (coincide con Iterable.expand); fxdart también acepta la grafía flatMap de FxTS: son el mismo operador.

Desviación respecto a FxTS: en FxTS, el callback puede devolver cualquier mezcla de valores sueltos e iterables y el operador deduce qué aplanar mediante la magia de tipos de DeepFlat. Dart no tiene equivalente de ese tipo condicional, así que el port a Dart exige que f devuelva siempre un Iterable<B> — exactamente como Iterable.expand. Devuelve una lista de un solo elemento ([x]) para emitir exactamente un valor por entrada, o una lista vacía para no emitir ninguno.

En el lado asíncrono, la máquina de estados interna de expandAsync tiene que recordar «qué subiterable estoy vaciando ahora mismo» entre extracciones, así que consume lo que tiene aguas arriba en serie: envolverlo en .concurrent(n) solo acelera la extracción de elementos ya disponibles, no un await que ocurra dentro del propio callback. Si necesitas trabajo asíncrono concurrente por elemento, hazlo en una etapa .map(...).concurrent(n) previa y después .expand((list) => list) para aplanar las listas ya resueltas — mira la Demo 2.

Demo 1 · Fundamentos

El callback debe devolver un Iterable: aquí, una lista de 2 elementos y una llamada a String.split:

Demo 2 · Asíncrono, la forma correcta de obtener concurrencia

Pon el await lento en una etapa .map(...).concurrent(n) y deja que expand aplane los resultados de forma síncrona:

Pruébalo tú

Ejercicio: usa expand para partir cada frase en palabras, produciendo una única lista plana de palabras.

Relacionado: map — transformar sin aplanar · flat — aplanar un iterable ya anidado · mapEffect — map para efectos secundarios · concurrent — evaluación en paralelo