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)]
Related
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
}
I often need to either shorten or pad a List to a certain amount of entries. For that I use a function like this:
fun List<String>.compactOrPadEnd(size: Int): List<String> {
if (this.size < size)
return this + List(if (this.size < size) size - this.size else 0) { "" }
else
return this.subList(0, size - 1) + this.subList(size - 1, this.size).joinToString("")
}
val list0 = emptyList<String>()
val list1 = listOf("A")
val list2 = listOf("A", "B")
val list3 = listOf("A", "B", "C")
val list4 = listOf("A", "B", "C", "D")
val list5 = listOf("A", "B", "C", "D", "E")
val size = 3
list0. compactOrPadEnd(size).onEach(::println) // [ , , ]
list1. compactOrPadEnd(size).onEach(::println) // [A, , ]
list2. compactOrPadEnd(size).onEach(::println) // [A, B, ]
list3. compactOrPadEnd(size).onEach(::println) // [A, B, C]
list4. compactOrPadEnd(size).onEach(::println) // [A, B, CD]
list5. compactOrPadEnd(size).onEach(::println) // [A, B, CDE]
The above code is more readable with separate functions:
fun List<String>.padEnd(size: Int) =
this + List(if (this.size < size) size - this.size else 0) { "" }
fun List<String>.compact(size: Int) =
this.subList(0, size - 1) + this.subList(size - 1, this.size).joinToString("")
fun List<String>.compactAndPadEnd(size: Int): List<String> =
if (this.size < size) padEnd(size) else compact(size)
I find both solutions too clumsy. I went through all the built-in collection functions to come up with something simpler, but to no avail.
Small side question: is there a better name than compactAndPadEnd?
You can write this as one case (i.e. without if-else) if you take advantage of the fact that joinToString happens to return your pad element "" when the list is empty.
fun List<String>.resizeEnd(size: Int): List<String> =
this.subList(0, min(size - 1, this.size)) +
this.subList(min(size - 1, this.size), this.size).joinToString("") +
List(max(0, size - this.size - 1)) { "" }
Notice that I'm creating a list of size size - this.size - 1 at the end. -1 because one of the empty strings would have been the one returned by joinToString("").
If you don't mind drop and take creating extra lists, you can make it shorter:
fun List<String>.resizeEnd(size: Int): List<String> =
this.take(size - 1) +
this.drop(size - 1).joinToString("") +
List(max(0, size - this.size - 1)) { "" }
You can also generalise this to:
fun <T> List<T>.resizeEnd(size: Int, padElement: T, foldFunction: (T, T) -> T): List<T> =
this.subList(0, min(size - 1, this.size)) +
this.subList(min(size - 1, this.size), this.size).fold(padElement, foldFunction) +
List(max(0, size - this.size)) { padElement }
But the catch is that padElement must be the identity for foldFunction.
As an alternative, you could create a List of fixed size using an initializer function combined with a when to initialize each item:
fun List<String>.resize(size: Int) = List(size) {
when {
it == size - 1 && this.size > size -> this.subList(size - 1, this.size).joinToString("")
this.size > it -> this[it]
else -> ""
}
}
I'm not entirely sure if this is any less clumsy, I suppose that depends on personal preference.
I see some slight clean-up you can do on your function. You're redundantly checking this.size < size, you could lift return out of the condition branches, and you could use take/drop for brevity.
fun List<String>.compactOrPadEnd(size: Int): List<String> {
return if (this.size < size)
this + List(size - this.size) { "" }
else
take(size - 1) + drop(size - 1).joinToString("")
}
Personally, I'd call it concatEndOrPad.
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 have two immutable maps, as example:
val a = mapOf("a" to 1, "z" to 1)
val b = mapOf("b" to 1, "a" to 1, "c" to 1)
Is there any elegant way to concatenate these maps and have a single map with all the keys? If it has duplicated keys, I want to replace it with value from B map.
The result should look something like:
mapOf("z" to 1, "b" to 1, "a" to 1, "c" to 1)
simple + operator:
/**
* Creates a new read-only map by replacing or adding entries to this map from another [map].
*
* The returned map preserves the entry iteration order of the original map.
* Those entries of another [map] that are missing in this map are iterated in the end in the order of that [map].
*/
public operator fun <K, V> Map<out K, V>.plus(map: Map<out K, V>): Map<K, V> =
LinkedHashMap(this).apply { putAll(map) }
example:
fun main(args: Array<String>) {
val a = mapOf("a" to 1, "z" to 1)
val b = mapOf("b" to 1, "a" to 2, "c" to 1)
val c = a+b
println(c)
}
output: {a=2, z=1, b=1, c=1}
I have a List of String, I want to transform into a Map of occurrences. ( ~ The Map values are the count of many times the String was repeated in the List)
The imperative way, I'd write like the following
fun transformMap(list: List<String>): Map<String, Int> {
val map = mutableMapOf<String,Int>()
for(n in list){
map.put(n,map.getOrDefault(n,0) + 1)
}
return map.toMap()
}
How to write this in Functional Programming way ?
In Java 8+, this will be written like this
String[] note;
Map<String, Integer> noteMap = Arrays.stream(note)
.collect(groupingBy(Function.identity(),
collectingAndThen(counting(), Long::intValue)));
You can use Kotlin's Grouping to do this in one line via the Iterable<T>.groupingBy extension:
val myList = listOf("a", "b", "c", "a", "b", "a")
val myMap = myList.groupingBy { it }.eachCount()
println(myMap)
// Prints {a=3, b=2, c=1}
You can use streams in Kotlin too. But if you want to avoid streams, you can use fold():
val list = listOf("a", "b", "c", "a")
val histogram = list.fold(mutableMapOf<String, Int>()) { map, s ->
map[s] = map.getOrDefault(s, 0) + 1
map
}.toMap()
println(histogram)