Is there something like `Flow<T>.distinctUnt...
# coroutines
m
Is there something like
Flow<T>.distinctUntilChanged(areEquivalent: (old: T, new: T) -> Boolean)
but emits the newest of the equivalent values instead of the oldest?
Use case: a search box with live search results. I want to capture search history items but of course you don’t want to capture an item for each keystroke, so if an items is a prefix of the following item, then treat that prefix as an “equivalent” and so ignore it.
a
this sounds underspecified. Implemented as you described in your first message, you have something that is equivalent to having no operator at all: "emits the newest of the equivalent values" simplifies to, "always emit the latest value" i.e. "no operator"
s
have you tried
debounce()
instead? (typically in combination with
mapLatest
) that way you ignore keystrokes for a certain time duration. That's the pattern I typically use for search with autocomplete
2
a
and yeah, that ☝️ - if you ignored any later emission with the same prefix you'd never see the more specific search query to run, so that's not what you want either
d
Not quite an answer but for my search boxes, instead of using
debounce
I just do
mapLatest
with a delay in it before the actual fetching.
1
a
Yeah the *Latest family of functions are great for this sort of thing and unique to flows if you're used to rx
s
unique to flows if you're used to rx
actually
mapLatest
is similar to
switchMap/switchMapSingle
depending on plain on suspend function
j
Yeah in Rx you would do switchMap with a delay on the inner observable. Latest in Flow is switch in Rx
m
Thanks all. In my case, I already use
debounce
, however, it’s not enough because there is no value that can adequately distinguish between a slow typer (this is an east asian language-learning app where the learner could legitimately take many seconds to type a character) and someone who is looking up a word, seeing the results, and quickly moving on. Debounce works well when the user is typing fast or backspacing the search text to oblivion. So I need some best-effort logic that will discard search text that is clearly a stepping stone to what the user is actually aiming for, though this logic is beyond the scope here. Regarding the part about never emitting the final value, my initial thought was that’s okay because as soon as the user clears the search box, then it’s emitted, and that leaving the app (
onPause
) could act as a trigger to flush the current search text. However, that latter part sounds messy, and makes me lean towards placing the discarding logic in the data source where it would potentially replace the latest history item instead of just adding.
c
In case this is useful to you, fyi there is a
debounce
overload which accepts a lambda
(T) -> Long
where you can set the debounce value dynamically based on the value emitted. https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/debounce.html
m
Thanks @Chris Fillmore although I already use this