Just wanted to share something that really impress...
# announcements
g
Just wanted to share something that really impressed me about Kotlin having only worked with the language a few days: I was told to add a semi-complex date sort to an array of event objects, where events that have not happened yet are first, in ascending order. Then, events that are currently happening, followed by events that have already happened. Started to write a write a really janky lambda custom comparator, but then I found out you can just do this:
Copy code
val sortedGames = games.sortedWith(compareBy{
    Instant.now().compareTo(it.dateTime)
})
e
If you want to make it even simpler you could try:
Copy code
val now = Instant.now()
val sortedGames = games.sortedBy { now > it.dateTime }
In kotlin the
>
and
<
operators just use
.compareTo()
😉
👍 1
g
Oh wow. 🤦
For some reason, this does not put the event that is happening at the end though (but the rest works)
Copy code
import java.util.*
import java.time.Instant

val gameTimes = listOf(
    "2020-03-09T08:00:00.000Z",
    "2020-03-09T09:00:00.000Z",
    "2020-03-09T10:00:00.000Z",
    "2020-03-09T11:00:00.000Z",
    "2020-03-09T12:00:00.000Z",
    "2020-03-09T13:00:00.000Z",
    "2020-03-09T14:00:00.000Z"
).map {  Instant.parse(it)  }

val currentTime = Instant.parse("2020-03-09T10:00:00.000Z")
val sortedGames1 = gameTimes.sortedWith(compareBy{
    currentTime.compareTo(it)
})

val sortedGames2 = gameTimes.sortedBy {
    currentTime > it
}

println(sortedGames1)
println(sortedGames2)
Copy code
sortedGames1: [2020-03-09T11:00:00Z, 2020-03-09T12:00:00Z, 2020-03-09T13:00:00Z, 2020-03-09T14:00:00Z, 2020-03-09T10:00:00Z, 2020-03-09T08:00:00Z, 2020-03-09T09:00:00Z]

sortedGames2:
[2020-03-09T10:00:00Z, 2020-03-09T11:00:00Z, 2020-03-09T12:00:00Z, 2020-03-09T13:00:00Z, 2020-03-09T14:00:00Z, 2020-03-09T08:00:00Z, 2020-03-09T09:00:00Z]
j
Copy code
val sortedGames2 = gameTimes.sortedBy(currentTime::compareTo)
The
.sortedBy
is just syntactic sugar for
.sortedWith(compareBy...
g
@Jakub Pi So why do these give different responses 🤔
One puts the current event in the first slot instead of sending it after the upcoming events, the other puts it in the proper place at the end
Actually my answer still is not right =/
j
compareTo gives a positive, 0 or negative response depending on the result of the comparison. So if you are basing your sort on just the currentTime, it may be that all past events are considered equal to each other, all current events are equal to each other and all future events are equal to each other. The sort is stable, so within those categories, ordering is preserved as in the original list.. You may need to pre-sort your collection using its natural ordering.
g
The order is supposed to be: 1. Upcoming Events 2. Happening Now 3. Passed Events Which, now that I think about it, is more like a
partitionBy
to separate the list into two chunks for which events are past/upcoming. I am not sure if Kotlin has a partition function.
Oh okay, it totally does. Yeah that'll be way easier 🤦
Copy code
val (upcoming, past) = games.partition {
        it.dateTime.isAfter(currentTime)
    }
j
Your current events will get put into the past bucket. You can look at groupBy to do the split three ways
Copy code
val sortedGames = gameTimes
        .groupBy{ it.compareTo(currentTime).sign}
        .mapValues{ (k, v) -> v.sorted() }
    println (listOf(1,0,-1)
        .flatMap{sortedGames
            .getOrElse(it, {emptyList()})})
g
💯 I realized that
groupBy
was the way the go about 15 mins ago but was having trouble figuring out how to do the actual grouping code
Thank you 🙏
j
There's a lot going on there, but code should handle all the edge cases (i.e. no future games in the list, or differences in times that are less than a second off)
g
Why can't I do this:
Copy code
val (past, happening, upcoming) = gameTimes.groupBy {
    currentTime.compareTo(it)
}.values
error: destructuring declaration initializer of type Collection<List<Instant!>> must have a 'component1()' function
Oh I see, destructuring actually compiles to
whatever.component1()
, etc under the hood. List type implements these functions, Collection does not. So I just needed to convert it to List:
Copy code
val (past, happening, upcoming) = gameTimes.groupBy {
    currentTime.compareTo(it)
}.values.toList()
💯
j
You can't guarantee the ordering of the map returned by groupBy, not necessarily sorted
That's why I did the explicit key lookup using the listOf(1,0,-1)
g
You can't guarantee the ordering of the map returned by groupBy, not necessarily sorted
What does this mean? When I did
println
on it, it was
-1 -> (past), 0 -> (happening). 1-> (upcoming)
, but you are saying that there are cases in which this could change?
Also is there any difference between
.toList()
and
listOf()
besides personal style preference?
j
Yes, it's not guaranteed. It may come back in any order. It may come back in the order of insertion if the underlying implementation uses a LinkedHashMap, but I don't think there's any guarantee
The .toList() is acting on something to convert to a list. listOf() just creates a list from scratch
g
Ahhh okay. Yeah probably safer to be explicit about the key mapping then rather than destructure.
j
Also, don't disregard the .sign I had. compareTo may return large numbers, not just 1,0, and -1
g
Untitled
Got it 👍
👍 1