Show how property references combine with collection operators (sortedBy, groupBy, associateBy, maxByOrNull) and explain what happens at the type level.
answer
- Selectors are (T) -> K; Person::age fits
- groupBy -> Map<K, List<T>>, associateBy -> Map<K, T>
- Key type inferred from the property's return type
- compareBy(Person::city, Person::age) for multi-key sort
- sortedBy needs Comparable; else sortedWith
basics
~10 sMany collection functions take a 'selector' function that returns a value per item. A property reference like Person::age fits perfectly: people.sortedBy(Person::age) sorts by age, people.groupBy(Person::city) groups by city.
solid answer
~30 sCollection operators such as `sortedBy`, `maxByOrNull`, `minByOrNull`, `groupBy`, `associateBy`, `distinctBy`, and `sumOf` take a **selector** `(T) -> K`. Because `Person::age` is a `KProperty1<Person, Int>` that behaves as `(Person) -> Int`, it drops straight in: `people.sortedBy(Person::age)`. `groupBy(Person::city)` yields `Map<String, List<Person>>`; `associateBy(Person::id)` yields `Map<Id, Person>`. The compiler infers the key type from the property's return type, keeping everything type-safe with no lambda boilerplate. For `Comparable` keys, `sortedBy`/`maxByOrNull` work directly; otherwise use `sortedWith(compareBy(Person::age))` to combine multiple selectors.
code
kotlin · 4 linesdata class P(val city: String, val age: Int)
val ps = listOf(P("NY", 30), P("LA", 20))
val ordered = ps.sortedWith(compareBy(P::city, P::age))
val byCity = ps.groupBy(P::city) // Map<String, List<P>>go deeper
Can use people.sortedBy(Person::age) and people.map(Person::name).
Knows return types of groupBy/associateBy and the inferred key type.
Combines references with compareBy for multi-key sorts and handles Comparable/nullable rules.
Designs key-extraction APIs around references for type-safe, refactor-proof pipelines.
## Selectors and references Most Kotlin **collection** transforms accept a **selector**: a function `(T) -> K` that extracts a key or value from each element. A property reference is exactly such a function, so it slots in with no lambda. ```kotlin data class Person(val id: Int, val name: String, val age: Int, val city: String) val people: List<Person> = load() val byAge = people.sortedBy(Person::age) // List<Person>, ascending age val oldest = people.maxByOrNull(Person::age) // Person? val byCity = people.groupBy(Person::city) // Map<String, List<Person>> val byId = people.associateBy(Person::id) // Map<Int, Person> val names = people.map(Person::name) // List<String> val ages = people.sumOf(Person::age) // Int val cities = people.distinctBy(Person::city) // unique-by-city ``` ## What happens at the type level - `Person::age` has type `KProperty1<Person, Int>`, which is assignable to `(Person) -> Int`. - `groupBy(keySelector: (T) -> K)` infers `K = String` from `Person::city`, producing `Map<String, List<Person>>`. - `associateBy(keySelector: (T) -> K)` infers `K = Int`, producing `Map<Int, Person>` (later duplicates overwrite earlier ones). - `sortedBy`/`maxByOrNull` require the selector's result to be `Comparable`; `Int`, `String`, etc. satisfy this. ## Multiple keys A single property reference is one key. For composite ordering use **`compareBy`** with several references: ```kotlin val ordered = people.sortedWith(compareBy(Person::city, Person::age)) ``` `compareBy(vararg selectors)` accepts references directly and chains them. ## Why this is idiomatic - **Type-safe**: the key type flows from the property's declared type; rename the property and the compiler flags every use. - **Concise**: `Person::age` reads as a field name, not as `{ it.age }`. - **Composable**: references combine with `compareBy`, `thenBy`, and standard operators. ## Gotcha `sortedBy` needs a `Comparable` selector result. If you select a non-comparable type, use `sortedWith` and supply a `Comparator`. For nullable selector results, the standard ordering puts `null` first (`nullsFirst`) unless you specify otherwise.
- What type does people.associateBy(Person::id) return, and what about duplicate ids?Map<Int, Person>. With duplicate keys, the last element with that id wins, overwriting earlier ones.
- How do you sort by two properties using references?Use sortedWith(compareBy(Person::city, Person::age)); compareBy chains the selectors left to right.
saying these in an interview costs you the question
- Thinking associateBy keeps all duplicates (it overwrites)
- Believing sortedBy works on non-Comparable selector results
- Saying you must write { it.age } because references don't fit selectors
- Not knowing groupBy returns Map<K, List<T>> vs associateBy Map<K, T>