the Kotlin standard library provides a variety of functions for performing operations on collections. You can see that there are 98 functions in total just below. We can divide them into conversion, filtering, addition and subtraction, grouping, take a part of the collection, get a single element of the collection, collection sorting, collection aggregation operations of these parts, and respectively to introduce their functions.


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 }

By hbb

Leave a Reply

Your email address will not be published. Required fields are marked *