I need to destructure Kotlin nested pairs. How can I do this simply without using pair.first/pair.second?
val chars = listOf('A', 'B', 'C')
val ints = listOf(1, 2, 3)
val booleans = listOf(true, false, false)
val cib: List<Pair<Pair<Char, Int>, Boolean>> = chars.zip(ints).zip(booleans)
cib.forEach { ((c, i), b) -> // compile error
println("$c $i $b")
}
Not sure if there really is a way of desctructuring a Pair<Pair<*,*>> straight away, but you could do this:
cib.forEach { (pair, b) ->
val (c, i) = pair
//do stuff with c, i, b
}
Related
I want to be able to check that collection a contains exactly all of the elements of b, but an equality based check is not sufficient; for example:
data class Person(val name: String, val age: Int)
val a = listOf(Person("Alice", 10), Person("Bob", 13))
val b = listOf(Person("Alice", 10), Person("Alice", 10))
fun main() {
println(a.containsAll(b))
}
true
Whilst this is technically true, it's not the result I want, because a only contains one Person("Alice", 10), whereas b contains two of them.
The above example should fail, whilst the below should pass.
data class Person(val name: String, val age: Int)
val a = listOf(Person("Alice", 10), Person("Alice", 10), Person("Bob", 13))
val b = listOf(Person("Alice", 10), Person("Alice", 10))
fun main() {
println(a.containsAllExact(b))
}
Is there a way to do this?
You could add an extension function for this, something like:
fun List<Person>.containsAllExact(list2: List<Person>): Boolean {
val occurences1 = this.groupingBy{ it }.eachCount()
val occurences2 = list2.groupingBy{ it }.eachCount()
return occurences2.all{
it.value <= occurences1.getOrDefault(it.key, 0)
}
}
One way I can think of is to make a mutable copy of a, and try to remove every element of b, then check if all elements of b can be removed:
println(
a.toMutableList().let { aCopy ->
b.all(aCopy::remove)
}
)
Or as an extension function:
fun <T> Iterable<T>.strictlyContainsAll(other: Iterable<T>) =
toMutableList().let { copy ->
other.all(copy::remove)
}
I think this would do it
println(a.containsAll(b) && (a.size - b.size == (a-b).size))
Edit: it doesn't work because it gives false negatives also. for example with
val a = listOf(Person("Alice", 10), Person("Alice", 10), Person("Bob", 13))
val b = listOf(Person("Alice", 10))
I have a function that returns Pair:
fun createTuple(a: Int, b: Int): Pair<Int, Int> {
return Pair(a, b)
}
I want to initialize variables a and b using this function and then reassign them inside loop:
var (a, b) = createTuple(0, 0)
for (i in 1..10) {
createTuple(i, -i).let{
a = it.first
b = it.second
}
println("a=$a; b=$b")
}
Using let seems awkward. Is there a better way to unwrap Pair inside loop?
The following lines do not compile:
(a, b) = createTuple(i, -i)
a, b = createTuple(i, -i)
var (a, b) = createPair(0, 0) compiles fine for me.
Your problem probably is using createTuple(i, -i) instead of createPair(i, -i).
I want to use reduce/fold methods to create a new list from an existing List. This is the code I tried to write:
val list: List<Int> = listOf(1, 2, 3)
val newList = mutableListOf<Int>()
val sum: List<Int> = list.fold(newList) { (acc: List<Int>, i: Int) -> {
acc.add(i + 10)
acc
}}
It doesn't compile.
I want that newList will be (11,12,13).
How can I do it in a functional manner?
In Javascript, for example, this code compiles:
list.reduce((acc, item) => {
acc.push(item + 10)
return acc;
}, [])
The most convenient and functional way to do what you want is using map function:
val list: List<Int> = listOf(1, 2, 3)
val newList: MutableList<Int> = list.map { it + 10 }.toMutableList()
But if you really want to use fold for some (strange) reason, you can do it this way:
val newList: MutableList<Int> =
list.fold(ArrayList()) { acc, x -> acc.apply { add(x + 10) } }
Or this way:
val newList: MutableList<Int> =
list.fold(ArrayList()) { acc, x ->
acc += x + 10
acc
}
I want to pair items in a list with one another
Example
list("A","B","C") to -> list(Pair(A,B),Pair(A,C),Pair(B,C))
list("A","B","C","D") to -> list(Pair(A,B),Pair(A,C),Pair(A,D),Pair(B,C),Pair(B,D),Pair(C,D))
I have tried using zipWithNext, but it does not help my cause. If anyone can show me how I can achieve this?
You can simply nest for loops and use ranges for that:
fun permute(list: List<String>): List<Pair<String, String>> {
var result: MutableList<Pair<String, String>> = mutableListOf()
for (i in 0..(list.size - 1)) {
val s = list.get(i)
for (j in (i + 1)..(list.size - 1)) {
val p = Pair(s, list.get(j))
result.add(p)
}
}
return result
}
There might be ways that are more Kotlin style, but I don't know one at the moment...
Using this method in a fun main() like this
fun main() {
val list = listOf("A", "B", "C", "D")
println(permute(list))
}
will output
[(A, B), (A, C), (A, D), (B, C), (B, D), (C, D)]
kotlin way )
var a = listOf("A", "B", "C", "D")
var pairs = a.mapIndexed { index, s ->
a.slice(index + 1 until a.size).map { Pair(s, it)}
}.flatten()
print(pairs)
If you were looking for a Pair chaining here is how to do it:
fun main() {
val a = listOf("A", "B", "C", "D")
val listPair: MutableList<Pair<String, String>> = mutableListOf()
a.forEachIndexed{ index, _ ->
if (index != a.size - 1) {
val pair = Pair(a.get(index), a.get(index + 1))
listPair.add(pair)
}
}
println(listPair)
}
Result: [(A, B), (B, C), (C, D)]
Is there a standard operation in Kotlin stdlib which would allow to iterate over a zip of 3 (or more) lists?
Effectively it should do:
list1.zip(list2).zip(list3) { (a, b), c -> listOf(a, b, c)}
Here are functions in the style of the standard library that do this. I'm not saying these are particularly optimized, but I think they're at least easy to understand.
/**
* Returns a list of lists, each built from elements of all lists with the same indexes.
* Output has length of shortest input list.
*/
public inline fun <T> zip(vararg lists: List<T>): List<List<T>> {
return zip(*lists, transform = { it })
}
/**
* Returns a list of values built from elements of all lists with same indexes using provided [transform].
* Output has length of shortest input list.
*/
public inline fun <T, V> zip(vararg lists: List<T>, transform: (List<T>) -> V): List<V> {
val minSize = lists.map(List<T>::size).min() ?: return emptyList()
val list = ArrayList<V>(minSize)
val iterators = lists.map { it.iterator() }
var i = 0
while (i < minSize) {
list.add(transform(iterators.map { it.next() }))
i++
}
return list
}
Usage:
val list1 = listOf(1, 2, 3, 4)
val list2 = listOf(5, 6)
val list3 = listOf(7, 8, 9)
println(zip(list1, list2, list3)) // [[1, 5, 7], [2, 6, 8]]