Kotlin - Col·lecció

Una col·lecció és un conjunt de dades.

Introducció

A Llista (i Map) vas veure una de les col·leccions més utilitzades.

Les col·leccions tenen funcions genèriques semblants a les que vam veure a l’activitat Function type.

Iterar

forEach

data class Person(val name: String, val age: Int)
val list = listOf(Person("Joan", 25), Person("Laura", 30), Person("Mireia", 35)))
list.forEach { println(it.name) }

Transformar

La biblioteca estàndard de Kotlin proporciona un conjunt de funcions d’extensió per a transformacions de col·leccions.

Aquestes funcions construeixen col·leccions noves a partir d’altres existents segons les regles de transformació proporcionades.

map

La transformació de mapatge crea una col·lecció a partir dels resultats d’aplicar una funció als elements d’una altra col·lecció.

La funció bàsica de mapatge és map(): aplica la funció lambda donada a cada element i retorna la llista dels resultats.

L’ordre dels resultats és el mateix que l’ordre original dels elements.

assert(
listOf(1, 2, 3).mapNotNull { if (it == 2) null else it * 3 }
== listOf(3, 6)
)

Si la transformació produeix null en determinats elements, pots eliminar els null de la col·lecció resultant cridant la funció mapNotNull() en comptes de map():

assert(
listOf(1, 2, 3).mapNotNull { if (it == 2) null else it * 3 }
== listOf(3, 6)
)

Map

Quan transformes un Map, tens dues opcions: transformar les claus mantenint els valors sense canvis i a l’inrevés.

Per exemple, si tens un registre d’estudiants amb la seva nota:

val students = mapOf("David" to 10, "Mireia" to 8, "Laura" to 3, "Laia" to 5)

Amb la funció mapValues() pots apujar la nota de tots els estudiants que han tret una nota igual o superior a 5:

assert(
students.mapValues { (_, grade) ->
if (grade in 5..<10) grade + 1 else grade
}
== mapOf("David" to 10, "Mireia" to 9, "Laura" to 3, "Laia" to 6)
)

I amb la funció mapKeys() pots posar en minúscula els noms dels estudiants:

assert(
students.mapKeys { (name, _) -> name.lowercase() }
== mapOf("david" to 10, "mireia" to 8, "laura" to 3, "laia" to 5)
)

Si vols pots fer les dues coses al mateix temps, una darrere de l’altre:

assert(
students.
mapKeys { (name, _) -> name.lowercase() }.
mapValues { (_, grade) ->
if (grade in 5..<10) grade + 1 else grade
}
== mapOf("david" to 10, "mireia" to 9, "laura" to 3, "laia" to 6)
)

Altres

Més informació a Transformations

Filtrar

El filtratge és una de les tasques més populars en el processament de col·leccions.

Les condicions de filtratge es defineixen mitjançant predicats: funcions lambda que prenen un element de la col·lecció i retornen un valor booleà: true vol dir que l’element compleix el predicat, false vol dir el contrari.

Filtrar per predicat

La funció bàsica de filtratge és filter().

Quan es crida amb un predicat, filter() retorna els elements de la col·lecció que el compleixen

A continuació tens una llista de mascotes:

enum class Type { DOG, CAT, TURTLE }
data class Pet(val name: String, val type: Type, val age: Int)
fun main() {
val pets = listOf(
Pet("Snoopy", Type.DOG, 3),
Pet(name = "Garfield", Type.CAT, age = 12),
Pet("Milú", Type.DOG, 10),
Pet(name = "Felix", type = Type.CAT, age = 10),
Pet("Harrite", Type.TURTLE, 90)
)
}

A continuació filtrem la llista amb només aquelles mascotes que són gossos:

assert(
pets.filter { it.type == Type.DOG }
== listOf(
Pet("Snoopy", Type.DOG, 3),
Pet("Milú", Type.DOG, 10)
)
)
Activitat

Filtra la llista amb només els gats que tenen més de 10 anys.

Activitat

Filtra la llista pets amb només gats i mapeja els resultats a una llista de noms de mascotes.

Per filtrar col·leccions per condicions negatives, utilitza filterNot().

Retorna una llista d’elements per als quals el predicat dona com a resultat false.

Per exemple, el nom de totes les màscotes que no són tortugues:

assert(
pets.filterNot { it.type == Type.TURTLE }.map { it.name }
== listOf("Snoopy", "Garfield", "Milú", "Felix")
)

filterNotNull() retorna tots els elements no nuls.

Si es crida sobre una List<T?>, filterNotNull() retorna una List<T: Any>, permetent-te tractar els elements com a no nuls.

assert(
listOf(null, "Joana", "Maria", null, "Pere").filterNotNull()
== listOf("Joana", "Maria", "Pere")
)

Test predicates

filterNotNull() returns all non-nullable elements.

Being called on a List<T?>, filterNotNull() returns a List<T: Any>, thus allowing you to treat the elements as non-nullable objects.

val numbers = listOf(null, "one", "two", null)
numbers.filterNotNull().forEach {
println(it.length) // length is unavailable for nullable Strings
}

Partition

Another filtering function – partition() – filters a collection by a predicate and keeps the elements that don’t match it in a separate list. So, you have a Pair of Lists as a return value: the first list containing elements that match the predicate and the second one containing everything else from the original collection.

val numbers = listOf("one", "two", "three", "four")
val (match, rest) = numbers.partition { it.length > 3 }
println(match)
println(rest)

Test predicates

Finally, there are functions that simply test a predicate against collection elements:

  • any() returns true if at least one element matches the given predicate.

  • none() returns true if none of the elements match the given predicate.

  • all() returns true if all elements match the given predicate. Note that all() returns true when called with any valid predicate on an empty collection. Such behavior is known in logic as vacuous truth.

val numbers = listOf("one", "two", "three", "four")
println(numbers.any { it.endsWith("e") })
println(numbers.none { it.endsWith("a") })
println(numbers.all { it.endsWith("e") })
println(emptyList<Int>().all { it > 5 }) // vacuous truth

any() and none() can also be used without a predicate: in this case they just check the collection emptiness. any() returns true if there are elements and false if there aren’t; none() does the opposite.

val numbers = listOf("one", "two", "three", "four")
val empty = emptyList<String>()
println(numbers.any())
println(empty.any())
println(numbers.none())
println(empty.none())

ordering

Ordering

aggregate

Aggregate operations

take

TODO

Continuem:

Sequence processing example