The essence of a mapping transformation is that a new set B is created from the result of a function on the elements of set A. As shown in the following figure, the functions associated with the mapping operation are map, mapNotNull, mapIndexed, mapIndexedNotNull, mapKeys, mapValuse.
The public functions for list, set, and map are described below:
function | intent |
---|---|
map | Applies the given lambda function to each subsequent element and returns a list of lambda results |
mapIndexed | Same as map, except that the parameters have element indices. |
mapNotNull | Same as map, except that it filters null |
mapIndexedNotNull | Same as mapIndexed, except that it filters null |
The code example is as follows:
val numbers = listOf(1, 2, 3)
numbers.map { it * 3 } // [3, 6, 9]
numbers.mapIndexed { idx, value -> value * idx } // [0, 2, 6]
numbers.mapNotNull { if ( it == 2) null else it * 3 } // [3, 9]
numbers.mapIndexedNotNull { idx, value -> if (idx == 0) null else value * idx } // [2, 6]
For map, you can also choose to convert only the key, or value, then you need to use mapKeys(), mapValuse() function.
val numbersMap = mapOf("key1" to 1, "key2" to 2, "key3" to 3)
val mapKeys = numbersMap.mapKeys { it.key.toUpperCase() } // {KEY1=1, KEY2=2, KEY3=3}
val mapValues = numbersMap.mapValues { it.value + it.key.length } // {key1=5, key2=6, key3=7}
As you can see from the figure, the essence of the merge transformation is to build pairs of elements with the same location in two collections, the default is to use Pair to store the values of the two collections, of course, we can also customize the merge transformation logic.
As shown in the following figure, the only functions for merge conversion are zip and unzip. zip is a merge operation, while unzip is a split operation.
The code example is as follows:
val list1 = listOf(1, 2, 3)
val list2 = listOf('a', 'b', 'c')
list1.zip(list2) // [Pair(1, 'a'), Pair(2, 'b'), Pair(3, 'c')]
list1.zip(list2) { t,v -> "$v = $t"} // ["a = 1" , "b = 2", "c = 3"]
val numberPairs = listOf("one" to 1, "two" to 2, "three" to 3, "four" to 4)
numberPairs.unzip() // Pair([one, two, three, four], [1, 2, 3, 4])
As you can see from the figure, the essence of an associative transformation is to construct a Map from collection elements and certain values associated with them.
Its corresponding functions are associateWith, associateBy, associate, as shown below:
function | intent |
---|---|
associateWith | Creates a Map with the collection value as key and the value generated by the function as value. keys are the same and the last one is kept. |
associateBy | Create a Map with the default collection value as value and the value generated by the function as key. you can customize the key-value generating function. the last one will be kept if the key is the same. |
associate | Creates a Map, which returns a Pair object |
The code example is as follows:
val numbers = listOf("one", "two", "three", "four","four")
numbers.associateWith { it.length } // {one=3, two=3, three=5, four=4}
numbers.associateBy { it.first().toUpperCase() } // {O=one, T=three, F=four}
numbers.associateBy(
keySelector = { it.first().toUpperCase() },
valueTransform = { it.length }
)
numbers.associate { it.first().toUpperCase() to it.last().toUpperCase() }
As shown in the figure, the essence of flatten conversion is to expand the nested collection elements. It corresponds to only two functions, flatten and flatMap. flatten function can be called on a collection of collections and returns a List of all the elements in the nested collection; flatMap requires a function as a parameter, and this parameter function maps the elements of a collection to another collection. So you can think of flatMap = map + flatten() as successive calls of
The code example is as follows:
val list = listOf(setOf(1, 2, 3), setOf(4, 5), setOf(6, 7))
list.flatten() // [1, 2, 3, 4, 5, 6, 7]
val map = mapOf(1 to 2, 3 to 4)
map.flatMap({ listOf(it.key, it.value) } ) // [1, 2, 3, 4]
string representation
String representation is the conversion of a collection into a string. kotlin provides two functions, joinToString and joinTo, which differ in that joinTo takes one more parameter to append the result to a given Appendable object. The code example is as follows:
val numbers = listOf("one", "two", "three", "four")
numbers.joinToString()
val stringBuilder = StringBuilder()
numbers.joinTo(stringBuilder)
numbers.joinToString(separator = " | ", prefix = "start: ", postfix = ": end")
numbers.joinToString {
"Element: ${it.toUpperCase()}"
}
Filtering is one of the most common set processing tasks, and in Kotlin, filtering conditions are defined by predicate functions.
Predicate Function: A lambda expression that takes a set element and returns a boolean value:true
means that the given element matches the predicate,false
means that it does not.
Filter by predicate function
function | intent |
---|---|
filter | Returns the elements of the set that match it, for Lists and Sets the result is a List, for Maps the result is still a Map. |
filterIndexed | Same as filter, except it comes with the position of the element in the collection |
filterNot | In contrast to the filter result, it uses negation to filter the collection and will return a list of elements for which the predicate function yields false. |
filterIsInstance | Returns a collection of elements of the specified T type |
filterNotNull | Returns a set of elements that are not null |
The code example is as follows:
val numbers = listOf("one", "two", "three", "four")
numbers.filter { it.length > 3 } // [three, four]
numbers.filterIndexed { index, s -> s.length > 3 } // [three, four]
numbers.filterNot { it.length <= 3 } // [three, four]
val numbers1 = listOf(null, 1, "two", 3.0, "four")
numbers1.filterIsInstance<String>()
numbers.filterNotNull()
There is only one function for partitioning, partition. partition filters the set by a predicate function and stores the unmatched elements in a separate list, which is saved in the Pair. The code looks like this
val numbers = listOf("one", "two", "three", "four")
// match 为 [three, four]
// rest 为 [one, two]
val (match, rest) = numbers.partition { it.length > 3 }
Test predicate function
There are three test predicate functions, any, none, and all, which act as the
function | intent |
---|---|
any | If at least one element matches the given predicate, then any() returns true. |
none | If no element matches the given predicate, then none() returns true. |
all | If all elements match the given predicate, then all() returns true, and calling all() on an empty set with any valid predicate function returns true. |
The code example is as follows:
val numbers = listOf("one", "two", "three", "four")
numbers.any { it.endsWith("e") } // true
numbers.none { it.endsWith("a") } // true
numbers.all { it.endsWith("e") } // false
emptyList<Int>().all { it > 5 } // true
In Kotlin, plus (+) and minus (-) operators are defined for sets. They take a set as the first operand; the second operand can be an element or another set. The return value is a new set.
The code example is as follows:
val numbers = listOf("one", "three", "two", "three","four")
numbers + "five" // [one, three, two, three, four, five]
numbers - listOf("three", "four") // [one, two]
numbers - "three" // [one, two, three, four]
val numbersMap = mapOf("one" to 1, "two" to 2, "three" to 3)
numbersMap + Pair("four", 4) // {one=1, two=2, three=3, four=4}
numbersMap.plus(Pair("one", 10)) // {one=10, two=2, three=3}
numbersMap + mapOf("five" to 5, "one" to 11) // {one=11, two=2, three=3, five=5}
numbersMap - "one" // {two=2, three=3}
numbersMap - listOf("two", "four") // {one=1, three=3}
numbersMap - mapOf("one" to 1) // {one=1, two=2, three=3}
There are only two grouping functions in Kotlin, groupBy and groupingBy. groupBy groups groups groups based on the value of the passed-in function as a key, and returns a Map. groupingBy returns Grouping, which allows you to manipulate all groups.
Grouping supports the following operations:
- eachCount() counts the elements in each group.
fold() and reduce() perform fold and reduce operations on each group as a single collection and return the result.
aggregate() – subsequently applies the given operation to all elements in each group and returns the result. This is a generic way to perform any operation on Grouping. It can be used to implement custom operations when collapsing or shrinking is not enough.
The code example is as follows:
val numbers = listOf("one", "two", "three", "four", "five")
// {O=[one], T=[two, three], F=[four, five]}
numbers.groupBy { it.first().toUpperCase() }
// {o=[ONE], t=[TWO, THREE], f=[FOUR, FIVE]}
numbers.groupBy(
keySelector = { it.first() },
valueTransform = { it.toUpperCase() }
)
// {o=1, t=2, f=2}
numbers.groupingBy { it.first() }.eachCount()
take a part of a set
slice
slice returns a list of collection elements with the given index. The index can be passed either as an interval or as a set of integer values. The order of the returned list is determined by the order of the input indexes.
The code example is as follows:
val numbers = listOf("one", "two", "three", "four", "five", "six")
numbers.slice(1..3) // [two, three, four]
numbers.slice(0..4 step 2) // [one, three, five]
numbers.slice(setOf(3, 5, 0)) // [four, six, one]
take vs. drop
function | intent |
---|---|
take | Fetches the specified number of elements from the beginning, and returns the entire collection when the called number is larger than the size of the collection |
takeLast | Get the specified number of elements starting from the end, when the number called is larger than the size of the set, the whole set will be returned |
takeWhile | A take with a judgment condition will keep fetching elements from the beginning until the judgment condition is not met. |
takeLastWhile | takeLast with a judgment condition, it will keep fetching elements from the end until the judgment condition is not met. |
drop | Remove a given number of elements from scratch |
dropLast | Remove a given number of elements from the tail |
dropWhile | In contrast to takeWhile, returns the first element between the first unsatisfied condition and the end. |
dropLastWhile | In contrast to takeLastWhile, returns the element between the beginning and the last unsatisfied condition. |
The code example is as follows:
val numbers = listOf("one", "two", "three", "four", "five", "six")
numbers.take(3) // [one, two, three]
numbers.takeLast(3) // [four, five, six]
numbers.takeWhile { !it.startsWith('f') } // [one, two, three]
numbers.takeWhile { it.startsWith('f') } // [] null
numbers.takeLastWhile { it != "three" } // [four, five, six]
numbers.drop(1) // [two, three, four, five, six]
numbers.dropLast(5) // [one]
numbers.dropWhile { it.length == 3 }) // [three, four, five, six]
numbers.dropLastWhile { it.contains('i') } // [one, two, three, four]
chunked
chunked can break a collection into chunks of a given size. The code example is as follows:
val numbers = listof(1, 2, 3, 4, 5, 6)
numbers.chunked(3) // [[0, 1], [2, 3], [4, 5]]
numbers.chunked(3) { it.sum() }
windowed and zipWithNext
windowed can get all the specified size of the specified window collection, the code example is as follows:
val numbers = listOf("one", "two", "three", "four", "five")
numbers.windowed(3)
The special zipWithNext function gets the set of two sized windows, with the following code example
val numbers = listOf("one", "two", "three", "four", "five")
// [(one, two), (two, three), (three, four), (four, five)]
numbers.zipWithNext()
numbers.zipWithNext { s1, s2 -> s1.length > s2.length }
Getting a single element of a collection
take sth. by its location
Fetch by position is to get the specified element by index, note that these functions are for list, set. The functions are described below:
function | intent |
---|---|
elementAt | Get the element at the specified position |
elementAtOrNull | elementAtOrNull() returns null when the specified position is out of the range of the collection |
elementAtOrElse | When the specified position is out of the range of the collection, the result of the passed-in function is returned |
The code example is as follows:
val numbers = listOf("one", "two", "three", "four", "five")
numbers.elementAt(0) // one
numbers.elementAtOrNull(5) // null
numbers.elementAtOrElse(5) { index -> "The value for index $index is undefined"}
take sth. according to its condition
function | intent |
---|---|
first | Gets the first element that satisfies the condition. The default is to get the first element directly. An exception is thrown if it is not found. |
firstOrNull | is the same as first, except that it returns null if no match is found. |
last | Get the last element that satisfies the condition, the default is to get the last element directly. An exception is thrown if it is not found. |
lastOrNull | and last, the difference being that if no match is found, null is returned. |
find | Equivalent to firstOrNull |
findLast | Equivalent to lastOrNull |
firstNotNullOf | First transform the collection, then get the first element that satisfies the condition. Failure to do so will throw an exception. |
firstNotNullOfOrNull | Same as firstNotNullOf, except that if no match is found, null is returned. |
The code example is as follows:
val numbers = listOf("one", "two", "three", "four", "five", "six")
numbers.first { it.length > 3 } // three
numbers.last { it.startsWith("f") } // five
numbers.firstOrNull { it.length > 6 } // null
numbers.find { it % 2 == 0 } // 2
numbers.firstNotNullOf { item -> item.takeIf { it.length >= 4 } } // three
randomize
random retrieves a random element of a collection. The code example is as follows:
val numbers = listOf(1, 2, 3, 4)
numbers.random()
numbers.random(Random(1))
Detecting the presence or absence of an element
function | intent |
---|---|
contains | To check for the existence of an element in a collection, you can use the in keyword to call contains as an operator. |
containsAll | Check if it contains elements from other collections |
isEmpty | Check if the collection is empty |
isNotEmpty | Check if the collection is not empty |
The code example is as follows:
val numbers = listOf("one", "two", "three", "four", "five", "six")
numbers.contains("four") // true
numbers.containsAll(listOf("four", "two")) // true
numbers.isEmpty() // false
numbers.isNotEmpty() // true
Sets Sorting
sorted
The sorted functions are described below:
function | intent |
---|---|
sorted | Sort the elements of the set in ascending order according to their natural order. |
sortedDescending | Sort the elements of the set in descending order according to their natural order. |
sortedBy | Ascending sorting by natural order of function return values |
sortedByDescending | Sort by the natural order of function return values in descending order |
sortedWith | Sort by incoming Comparator Comparator |
The code example is as follows:
val numbers = listOf("one", "two", "three", "four")
numbers.sorted()
numbers.sortedDescending()
numbers.sortedBy { it.length }
numbers.sortedByDescending { it.last() }
val lengthComparator = Comparator { str1: String, str2: String -> str1.length - str2.length }
numbers.sortedWith(lengthComparator)
reversed
Both the reversed and asReversed functions return collections in reverse order. The difference is that for mutable collections, changes to the collection returned by asReversed affect the original collection, while reversed does not. The code example is as follows
val numbers = listOf("one", "two", "three", "four")
numbers.reversed()
numbers.asReversed()
shuffled
The shuffled function returns a new List of collection elements sorted in random order. the code example is as follows:
val numbers = listOf("one", "two", "three", "four")
numbers.shuffled() // [four, three, one, two]
set aggregation operation
min, max, average, sum and count
function | intent |
---|---|
min | Returns the smallest value in the set |
minBy | Accepts a selector function and returns the element that minimizes the value returned by the selector. |
minWith | Accepts a Comparator object and returns the smallest element according to this Comparator object. |
minOf | Get the smallest value after the function has processed the collection elements |
max | Returns the largest value in the set |
maxBy | Accepts a selector function and returns the largest element that makes the selector returnable |
maxWith | Accepts a Comparator object and returns the largest element according to this Comparator object. |
maxOf | Get the largest value after the function has processed the collection elements |
average | Returns the average value of the elements in a collection of numbers |
sum | Returns the sum of the elements in a collection of numbers |
sumOf | The value of the accumulator function after processing the elements of the set |
count | Returns the number of elements in the set |
The code example is as follows
val numbers = listOf(6, 42, 10, 4)
println("Min: ${numbers.min()}") // Min: 4
println("Max: ${numbers.max()}") //Max: 42
val numbers = listOf(5, 42, 10, 4)
val min3Remainder = numbers.minBy { it % 3 }
println(min3Remainder)
val strings = listOf("one", "two", "three", "four")
val longestString = strings.maxWith(compareBy { it.length })
println(longestString)
val list = listOf(1, 2, 3)
println(list.minOf { it * 3 })
println("Average: ${numbers.average()}")
println("Sum: ${numbers.sum()}")
val numbers = listOf("one", "two", "three", "four")
println(numbers.sumOf { it.length })
Note that the min, minBy, minWith, and minOf functions report an error when used with the empty set. minOrNull, minByOrNull, minWithOrNull, and minOfOrNull can be used, and in the case of the empty set, they return null. The same applies to the max function.
reduce and fold
Both the reduce and fold functions work by sequentially applying the supplied functions to the elements of the set and returning the cumulative result. The difference is that the fold function takes an initial value and uses it as the first step in the accumulation; the reduce function does not.
The code example is as follows:
val numbers = listOf(5, 2, 10, 4)
val simpleSum = numbers.reduce { sum, element -> sum + element }
println(simpleSum)
val sumDoubled = numbers.fold(10) { sum, element -> sum + element * 2 }
println(sumDoubled)
If you want to apply the elements in reverse order, you can use the reduceRight and foldRight functions, as shown in the following code example:
val numbers = listOf(5, 2, 10, 4)
val sumDoubledRight = numbers.foldRight(1) { element, sum -> sum * element }
println(sumDoubledRight)
Note in particular that the position of the argument to sum, element
for reduceRight and foldRight is the opposite of fold and reduce. It is sum, element
in fold, reduce and element, sum
in reduceRight, foldRight.
If you want to get the current index, you need to use the reduceIndexed and foldIndexed functions. They also have their counterparts in reverse order, reduceRightIndexed and foldRightIndexed, as shown in the following code example:
val sumEven = numbers.reduceRightIndexed { idx, element, sum ->
println("$idx $sum $element")
if (idx % 2 == 0) sum + element else sum
}
println(sumEven)
println("====================================")
val sumEvenRight = numbers.foldRightIndexed(0) { idx, element, sum ->
println("$idx $sum $element")
if (idx % 2 == 0) sum + element else sum
}
println(sumEvenRight)
All reduce functions will report an error when used on the empty collection. If you want to return null on the empty set, you can use reduceOrNull, reduceRightOrNull, reduceIndexedOrNull, reduceRightIndexedOrNull.
If you want to get the intermediate results of the computation you can use runningReduce, runningReduceIndexed, runningFold, runningFoldIndexed. the code example is as follows:
val numbers = listOf(0, 1, 2, 3, 4, 5)
val runningReduceSum = numbers.runningReduce { sum, item -> sum + item }
val runningFoldSum = numbers.runningFold(10) { sum, item -> sum + item }