I tried searching here and youtrack with no result...
# stdlib
s
I tried searching here and youtrack with no results but has a
flatten()
with a
transform
been considered ? Seems like an obvious addition which would enable us to skip a list allocation (we have a bunch of
flatten().map { it.idOfEntity }
) not to mention it being a bit simpler:
flatten { it.idOfEntity }
A difference with normal
flatten
would be that it no longer calls
addAll()
which allows it to possibly skip a few grows but given that it can skip an entire copy on the next
map
in our case it seems like it should win out. Maybe that would be a reason for keeping both as overloads and not add transform with default parameter. Example implementation:
Copy code
public inline fun <T, R> Iterable<Iterable<T>>.flatten(transform: (T) -> R): List<R> {
    val result = ArrayList<R>()
    for (elementIterable in this) {
        for (element in elementIterable) {
            result.add(transform(element))
        }
    }
    return result
}
e
interesting idea. alternately, if there were a
Copy code
inline fun <T, R> Iterable<T>.transform(transform: MutableList<R>.(T) -> Unit): List<R> = buildList {
    for (it in this@transform) {
        transform(this, it)
    }
}
to align with
fun Flow<T>.transform(transform: suspend FlowCollector<R>.(T) -> Unit): Flow<R>
, you could use
Copy code
.transform { it.mapTo(this) { it.idOfEntity } }
although it does seem that type inference doesn't work here; hopefully that'll be addressed by https://youtrack.jetbrains.com/issue/KT-45618
s
I would say both could make sense on their own. Having
.transform { it.mapTo(this) { it.idOfEntity } }
instead of ie.
flatten().map { it.idOfEntity }
would really stand out. Its obviously not fair to compare such a flexible function like your
transform
but the mental load is quite large versus mine which imo fits quite well in with the rest of the collection mapping functions.
e
there isn't a
Sequence<Sequence<T>>.flatten(transform: (T) -> R)
or a
Flow<Flow<T>>.flatten(transform: (T) -> R)
, while there is a
Flow<T>.transform(...)
, so I figure that interface is already well known.
flatten+map
looks like an easy shortcut, but there's so many other things that could be more efficient if chained from
flatten
that I'm not sure this one is special enough. rather than adding flat versions of
.map/Indexed/NotNull/To
,
.min/max/Of/With/OrNull
,
.any/all/none
,
.associate/By/With/To
, etc., using
.asSequence().flatten()
first has some different tradeoffs but doesn't take up any more API space
s
I am not sure lack of methods in coroutines-core should be used as an argument for methods in stdlib. That library is larger than stdlib itself and I doubt even half of kotlin developers use it. In any case I think the case rests on why there is a
flatten()
in the first place when
flatMap { it }
is almost as short and probably identical in performance from what I can glean from the bytecode. Thanks for the talk
Youtrack issue with the request https://youtrack.jetbrains.com/issue/KT-49874
e
my argument about kotlinx.coroutines-core is more about - here are names and signatures we should use for consistency, and functions that have been needed often enough to justify adding to a common library.
flatMap
isn't strictly necessary, but it is common enough. obviously YMMV, but looking through my code, I do see a few places where
.flattenMapNotNull()
could be used, quite a lot of places where
.transform()
could be used, but no occurrences that could be written with
.flatten(transform)
. fine to file a feature request, but personally I don't find it convincing.