foldBy
Reduce los valores de cada clave en una sola pasada: el agregado, sin los grupos.
Lección
foldBy es fold ejecutado
una vez por clave en lugar de una vez sobre toda la fuente. Cada elemento
elige su clave y su valor se reduce dentro del acumulador de esa clave, así
que el resultado es un Map<K, Acc> de respuestas, no de
elementos.
La razón de que exista es lo que no hace.
groupBy seguido de una reducción por
grupo tiene que construir antes una List para cada clave:
asignación proporcional a la entrada para una respuesta
proporcional al número de claves. Si lo único que quieres es
el total por categoría, esas listas se construyen y se tiran.
foldBy acumula directamente en el mapa de resultado, como el
bucle escrito a mano:
// lo que escribirías a mano
for (final t in txns) {
totals[t.category] = (totals[t.category] ?? 0) + t.amount;
}
// lo mismo, con nombre
foldBy((Tx t) => t.category, 0.0, (sum, t) => sum + t.amount, txns);
Sobre un millón de transacciones repartidas en cinco categorías, agrupar
primero cuesta 2,7× el bucle escrito a mano.
foldBy cuesta 0,91× — es algo más
rápido que el bucle que tiene al lado, y no es un artefacto de
redondeo. El bucle lee el mapa y luego lo reescribe, así que cada
transacción calcula el hash de su categoría dos veces;
foldBy acumula en una celda mutable alojada en el mapa, de modo
que este se escribe una vez por categoría y no una vez por
transacción. Varios de los ejemplos de
Dart vs FxDart se pasaron a él
justo por eso.
No leas demasiado en ese margen: aquí el callback del fold es una suma, así
que el mapa es casi todo el trabajo. Con un acumulador más pesado el ahorro
sigue ahí, pero desaparece dentro del coste del propio callback — mira la
nota sobre records al final. La razón para elegir foldBy es que
dice lo que quieres decir; ser una pizca más rápido que el bucle es una
ventaja añadida, no el argumento.
Las claves salen en el orden en que aparecen por primera vez,
igual que en groupBy. No es un port de FxTS: la forma viene de
groupingBy().fold() de Kotlin.
fold, seed es un único valor que se usa como punto
de partida para todas las claves. Eso está bien con números y
cadenas, que reduces hacia valores nuevos. Una semilla mutable
— una lista, un conjunto, un mapa — la compartirían todas las claves y todas
la mutarían. Si necesitas acumular en una estructura mutable por grupo, usa
groupBy.
Demo 1 · Lo básico
Demo 2 · Asíncrono
Pruébalo tú
Ejercicio: la demo cuenta palabras por letra inicial.
Cámbiala para que sume las letras bajo cada inicial, de modo
que fig y fx den {f: 5}.
groupBy.