foldByOrSkip

Reduce por clave como foldBy, salvo que una clave null salta el elemento — así un solo callback selecciona y clasifica.

Map<K, Acc> foldByOrSkip<A, K extends Object, Acc>(K? Function(A a) key, Acc seed, Acc Function(Acc acc, A a) f, Iterable<A> iterable) Map<K, Acc> Fx<T>.foldByOrSkip<K extends Object, Acc>(K? Function(T a) key, Acc seed, Acc Function(Acc acc, T a) f) // chain // Strict: returns a Map, runs when called. A null key skips the element, so // `key` both selects and buckets — filter + foldBy in one call.

Lección

foldByOrSkip(key, seed, f, xs) es filter + foldBy escrito como una única llamada estricta. Todo lo que foldBy garantiza sigue en pie: seed inicia cada clave en lugar de arrastrarse entre ellas, las claves salen en orden de primera aparición y el mapa se consulta una vez por elemento. La única vuelta de tuerca es la función de clave: devolver null significa «salta este elemento», la misma forma filter_map que usa takeUniqBy.

Escribe filter(...).foldBy(...) por defecto. Dos pasos con nombre se leen mejor que un callback respondiendo a dos preguntas, y aquí esas dos preguntas suelen no tener relación: un rango de fechas y una categoría no son la misma idea. Este operador existe por un solo motivo, y conviene saber cuál es.

Por qué existe: el predicado que el compilador no puede ver

filter es una etapa perezosa, así que guarda su predicado en un campo del iterador. El compilador AOT no puede ver a través de un campo, de modo que ese predicado nunca se inserta: cada elemento paga una llamada indirecta real y su cuerpo jamás se funde con el bucle que lo rodea. foldBy no tiene ese problema: es estricto, así que sus callbacks son parámetros y sí se insertan. Lo que cuesta es el filtro que va delante.

foldByOrSkip mueve la prueba dentro de la clave, que es un parámetro. Medido sobre 1.000.000 de transacciones, AOT, conservando un mes de doce:

FormaTiempo
filter().foldBy()14,5 ms
foldByOrSkip(…)12,6 ms
un bucle escrito a mano11,3 ms

Ambas formas, y ambas barras, están en Informe mensual por categoría: una de las dos páginas de comparación que publican tres barras en lugar de dos, porque la diferencia entre dos maneras de escribir la misma tubería es precisamente lo que quieren mostrar.

Demo 1 · El gasto de julio por categoría

Demo 2 · La semilla, el salto y lo que ve el fold

Pruébalo tú

Ejercicio: la lectura más alta por sensor, ignorando las filas defectuosas.

Relacionado: foldBy — la reducción sobre la que se apoya · filter — la etapa que absorbe · takeUniqBy — la misma idea para filter + uniqBy + take · Rendimiento — de dónde sale el suelo de los callbacks