foldByOrSkip

foldBy처럼 키별로 접되, 키가 null이면 그 원소를 건너뜁니다 — 콜백 하나가 선택과 분류를 겸합니다.

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.

강의

foldByOrSkip(key, seed, f, xs)filter + foldBy를 하나의 즉시(strict) 호출로 쓴 것입니다. foldBy가 보장하는 것은 그대로입니다 — seed는 키마다 시작점이지 키를 가로질러 누적되지 않고, 키는 처음 등장한 순서로 나오며, 맵은 원소당 한 번만 조회됩니다. 한 가지 비틀림은 키 함수입니다: null을 반환하면 "이 원소는 건너뛴다"는 뜻으로, filter_map 형태이며 takeUniqBy가 같은 형태를 씁니다.

기본은 filter(...).foldBy(...)로 쓰십시오. 이름 붙은 두 단계가 두 질문을 겸하는 콜백 하나보다 잘 읽히고, 여기서는 그 두 질문이 대개 무관합니다 — 날짜 범위와 카테고리는 같은 생각이 아닙니다. 이 연산자가 존재하는 이유는 하나뿐이며, 그게 무엇인지 아는 편이 좋습니다.

존재 이유: 컴파일러가 볼 수 없는 술어

filter지연 단계라 술어를 이터레이터의 필드에 담아 둡니다. AOT 컴파일러는 필드 너머를 보지 못하므로 그 술어는 결코 인라인되지 않습니다 — 원소마다 실제 간접 호출을 내고, 본문이 주변 루프에 녹아들지도 못합니다. foldBy에는 그 문제가 없습니다. 즉시 연산자라 콜백이 매개변수이고 호출부에 인라인됩니다. 앞에 붙은 필터가 비용입니다.

foldByOrSkip은 그 판정을 매개변수인 키 안으로 옮깁니다. 거래 100만 건, 12개월 중 한 달만 남기는 조건, AOT 기준:

표현시간
filter().foldBy()14.5 ms
foldByOrSkip(…)12.6 ms
손으로 쓴 루프11.3 ms

두 표현과 두 막대 모두 월별 카테고리 리포트 페이지에 있습니다 — 같은 파이프라인을 쓰는 두 방식의 차이가 그 페이지가 말하려는 바라서, 막대를 둘이 아니라 셋 싣는 두 페이지 중 하나입니다.

데모 1 · 7월의 카테고리별 지출

데모 2 · seed, 건너뛰기, 그리고 fold가 보는 것

직접 해보기

연습: 고장 난 행을 빼고 센서별 최고 측정값 구하기.

관련: foldBy — 이 연산자가 딛고 선 접기 · filter — 흡수한 단계 · takeUniqByfilter + uniqBy + take에 대한 같은 발상 · 성능 — 콜백 바닥값이 어디서 오는지