`partition` only supports two outputs. What about ...
# stdlib
r
partition
only supports two outputs. What about adding something like this to the stdlib?
Copy code
/**
 * Similar to the stdlib partition method, but supports an arbitrary number of output lists. The number
 * of outputs must be known in advance.
 */
@Throws(IndexOutOfBoundsException::class)
fun <T> List<T>.partitionInto(size: Int, predicate: (T) -> Int): List<List<T>> {
  val lists = listOf(*(0.until(size).map { mutableListOf<T>() }.toTypedArray()))
  for (element in this) {
    val index = predicate(element)
    lists[index].add(element)
  }
  return lists
}
m
You can do that with
list.groupBy { predicate(it) }.values
e
and rename
predicate
to
keySelector
😉
âž• 2
m
If you need to have the List<List> indexable, you might have to sort/tweak the above a bit but I found that most of the times I wanted to partition with multiple values,
groupBy
wasdoing the job
r
Isn't there an ordering issue with that? The groupBy preserves the iteration order of the original map.
m
Yea not 100% sure about the order
r
Yeah don't think that would work
e
result[0]
,
result[1]
etc. will give you the correct lists regardless
r
Sure, if I was looking for a result of
Map<Int, List<T>>
, but that isn't "partition" then 🙂
e
if you want to use a List instead of a Map… yeah that will take a little bit of mangling, but not too bad:
Copy code
val map = groupBy { .. }
List(size) { map[it].orEmpty() }
m
What I like about partition is that I can use destructuring
Copy code
val (even, odd) = list.partition { it%2 == 0 }
With multiple buckets, destructuring isn't really possible so using
groupBy
is usually good enough
r
Yup, exactly on the destructuring -^
val (foo, bar, baz) = l.partitionInto(3) { ... }
m
Could that even work?
r
It does
Lists are destructurable
today i learned 1
e
up to... 22 components, I think
😅 1
m
I see
e
Copy code
val map = groupBy { ...toString() }
val `0` by map
val `1` by map
(yes, this actually works, but don't)
🙃 2
r
Lol
e
in any case, IMO this is specialized enough that
groupBy
with some follow-up transform is fine
r
I'd rather just implement
partiionInto
in my project locally, which I think is clearer than groupBy + transform.
val (foo, bar, baz) = l.partitionInto(3) { ... }
vs
Copy code
val map = groupBy { .. }
val (foo, bar, baz) = List(size) { map[it].orEmpty() }
e
the partition-like function that I do wish we had is a combined take/drop (and ***While variants), but that's probably not gonna get into stdlib either
r
Yeah, can't disagree there. Much better than messing with
withIndex
. Though using
withIndex
to do a combined drop+take is about as complex as the groupBy + transform above.
j
val lists=Array(size){mutableListOf<T>()}
😀
r
I like
val lists = List(size) { mutableListOf<T>() }
better, but that needs an extra
.map { it.toList() }
at the return.
👌 1
And the array variant has other issues
e
??
val a: List<List<Any>> = MutableList(10) { mutableListOf() }
works just fine, as does
val a: List<List<Any>> = Array<MutableList<Any>>(10) { mutableListOf() }.asList()
r
Hmm, so it does
j
i would probably roll
myList.fold(List(predicate.size){mutableListOf()}){a,it-> a.apply{this[predicate(it)]+=it}}
r
Sure, variants are possible but I was aiming for something as close to the current stdlib implementation of
partition
as possible. That is:
Copy code
val first = ArrayList<T>()
    val second = ArrayList<T>()
    for (element in this) {
        if (predicate(element)) {
            first.add(element)
        } else {
            second.add(element)
        }
    }
    return Pair(first, second)
j
predicate as a transform is uncanny
r
Yeah, the parameter should probably be renamed to
indexSelector
j
depending on the usefulness of the content you can just write the ordered list of transforms