Compose maps using key and value in Kotlin - kotlin

I have two maps and I want to merge using some rules
val a = mapOf(1 to 101, 2 to 102, 3 to 103)
val b = mapOf(101 to "a", 102 to "b", 103 to "c")
is there a way to merge using values for a and key for b
val result = mapOf(1 to "A", 2 to "b", 3 to "c")

Since the values of a and the keys of b will always match, you can do this with a single mapValues
val result = a.mapValues { (_, v) -> b.getValue(v) }
getValue can be safely used here since the key v always exist in b as a consequence of our assumption.
In general, if b may not have all the values of a as keys, you can do:
val result = a.entries.mapNotNull { (k, v) -> b[v]?.let { k to it } }.toMap()
This removes the key from the result, if the key's corresponding value in a doesn't exist as a key in b. You can also use this as an alternative if you don't like the !! in the first solution.
mapNotNull and toMap loops through the entries one time each. If that is a problem for you, use asSequence:
a.asSequence().mapNotNull { (k, v) -> b[v]?.let { k to it } }.toMap()

Since you're sure each value from map a is present in map b, this should be as simple as using mapValues and using the value of that key in map b
a.mapValues { entry ->
b[entry.value]
}
Due to having to edit and redo my answer, #Sweeper beat me to it and provided a more in-depth answer.

Related

Is there an easy way to multiply each element with each other in array / list - Kotlin?

I have got {1,2,3} / or it might be a list<Int> is there an easy way to multiply each element with each other like 1*2 , 1*3, 2*3 ?
This should work, given that you probably don't want to include the duplicates like items[i] * items[j] and items[j] * items[i]
val items = listOf(1, 2, 3, 4)
val result = items.flatMapIndexed { index, a ->
items.subList(index + 1, items.size).map { b -> a * b }
}
println(result) // [2, 3, 4, 6, 8, 12]
flatMapIndexed builds a list for each of the items elements by evaluating the lambda on the index and the item, and then concatenates the lists.
subList is an efficient way to take the items in the specific range: starting at the next index, and until the end of the list.
You can try the old-fashioned way: nested loops
fun main(args: Array<String>) {
val list = listOf( 1, 2, 3 )
for (i in list.indices) {
for (j in (i + 1) until list.size) {
println("${list[i]} * ${list[j]} = ${list[i] * list[j]}")
}
}
}
Output of this code:
1 * 2 = 2
1 * 3 = 3
2 * 3 = 6
If you did want all the permutations (every possible ordering), you could do something like this:
val result = with(items) {
flatMapIndexed { i, first ->
slice(indices - i).map { second -> // combine first and second here }
}
}
slice lets you provide a list of indices for the elements you want to pull, so you can easily exclude the current index and get all the other elements to combo it with. Takes an IntRange too, so you can do slice(i+1 until size) to get the combination (every pairing) functionality too.
Not as efficient as hotkey's subList version (since that doesn't make a copy) but you can get two behaviours this way so I thought I'd mention it! But if I were making a reusable function rather than a quick one-liner, I'd probably go with deHaar's approach with the nested for loops, it's efficient and easy enough to tweak for either behaviour

How to get collection values in Kotlin lambda

I have two collections: A and B. Both of them consist of equal amount of chars. I zip them. Then I need to count pairs of the same elements, like 'A' and 'A'. I need to write a predicate, but I can't find the way to get both elements from a zipped collection.
I've tried something like this:
val num = A.zip(B).count { it.i: Int, it.j:Int -> it.i == it.j}
and this:
val num = A.zip(guess).count { it[0] == it[2] }
But it doesn't work. How can I reach both elements from these sub lists of chars?
I looked into Kotlin official examples but there are only easy ones:
val evenCount = numbers.count { it % 2 == 0 }
In order to deconstruct the resulting Pair given by .zip when using .count, you need to put the "deconstructor" into parentheses.
a.zip(b).count { (aElement, bElement) -> aElemant == bElement }
You could also just ignore that and just access it directly.
a.zip(b).count { it.first == it.second }
If you want to count the pairs of elements in two lists, you're very close. By calling zip you are given a Pair. You can count the resulting list of Pair objects by accessing their first and second parts and seeing if they match (using ==).
// Assumptions...
val a = listOf("A", "B", "D")
val b = listOf("A", "B", "C")
// Count where both elements of the zipped pair match
return a.zip(b).count { it.first == it.second }

Generating unique random values with SecureRandom

i'm currently implementing a secret sharing scheme.(shamir)
In order to generate some secret shares, I need to generate some random numbers within a range. FOr this purpose, I have this very simple code:
val sharesPRG = SecureRandom()
fun generateShares(k :Int): List<Pair<BigDecimal,BigDecimal>> {
val xs = IntArray(k){ i -> sharesPRG.nextInt(5)}
return xs
}
I have left out the part that actually creates the shares as coordinates, just to make it reproduceable, and picked an arbitrarily small bound of 5.
My problem is that I of course need these shares to be unique, it doesnt make sense to have shares that are the same.
So would it be possible for the Securerandom.nextint to not return a value that it has already returned?
Of course I could do some logic where I was checking for duplicates, but I really thought there should be something more elegant
If your k is not too large you can keep adding random values to a set until it reaches size k:
fun generateMaterial(k: Int): Set<Int> = mutableSetOf<Int>().also {
while (it.size < k) {
it.add(sharesPRG.nextInt(50))
}
}
You can then use the set as the source material to your list of pairs (k needs to be even):
fun main() {
val pairList = generateMaterial(10).windowed(2, 2).map { it[0] to it[1] }
println(pairList)
}

Kotlin Is there a way to flatten Map<K out T , List<V out T> to List<T>

I would like to transform the Map to List
e.g I have
mapOf("a" to listOf(1,2),
"b" to listOf(3,4)
)
I want the result to be
listOf("a", 1, 2, "b", 3, 4)
order must be Key and its Values, Key and its Values, ...
is there some function in kotlin that could help me with that?
My second comment variant as answer for a Map<String, List<Int>>:
mapOf("a" to listOf(1,2),
"b" to listOf(3,4))
.flatMap { (key, values) -> listOf(key) + values }
which gives a List<Any> with the keys followed by their values.
This example makes use of destructuring declaration and Map.flatMap.
UPDATE: the answer below was written before the question was updated and changed how the map was created (see the history of the question for details). As the question now stands, the answer below will no longer work. It does work for the question as originally asked though, I believe.
#Roland is right that your map will never result in that list because there can only ever be a single value in the map against any given key. So I think you need to replace that map with a list of pairs. You can then group it and flatmap it to get your desired result:
val pairs = listOf("a" to 1, "a" to 2, "b" to 3, "b" to 4)
val result = pairs
.groupBy { it.first }
.flatMap { (key, values) -> listOf(key).plus(values.map { it.second }) }
Another slightly different option which you might decide is more readable is this:
val result = pairs
.groupBy({ it.first }, { it.second })
.flatMap { (key, values) -> listOf(key).plus(values) }
You can flatMap over map.entries. Have a look at this function:
val map = mapOf("a" to listOf(1,2),
"b" to listOf(3,4))
println(map)
val flattened : List<Any> = map.entries.flatMap {entry ->
//create list with key as only element, must be type <Any> to add other stuff later
val list = mutableListOf<Any>(entry.key)
//add values
list.addAll(entry.value)
list
}
println(flattened)
prints:
{a=[1, 2], b=[3, 4]}
[a, 1, 2, b, 3, 4]
#Rolands answer inspired this even simpler, more idiomatic version. It essentially does the same, but crams everything into one line:
val flattened: List<Any> = map.flatMap {entry ->
listOf(entry.key) + entry.value
}

Nested pair array in kotlin

I want to use nested pair in kotlin, such as "a" to {"b" to "c"}
I have tried :
"teachers" to {"a" to "c"; "c" to "d"}
but when I debug this, the data type is:
(teachers, () -> kotlin.Pair<kotlin.String, kotlin.String>)
how to use this?
if don't use
"a" to mapOf("a" to "b"...)
Is it possible?
{ A -> B } is an anonymous function whose parameter is A and body is B.
{ B } is a short form of { () -> B }.
In addition,
A; B
is the same as
A
B
Therefore {"a" to "c"; "c" to "d"} means a function whose parameter list is () (zero parameters) and body is
Pair("a", "c")
Pair("c", "d")
which is equivalent to something like ::noName with
fun noName() {
Pair("a", "c")
return Pair("c", "d")
}
Anyway, what are the braces in your code? They do not mean "pairs." I think you meant to represent a map (or dictionary) in python, but PAIRS ARE NOT MAPS and vice versa.
Nested pair is something like this: "a" to ("b" to "c") which is equivalent to Pair("a", Pair("b", "c"))
If you want to make a map in Kotlin, you should use function mapOf().
If you want to make an array of pairs in Kotlin, you can do so like arrayOf("a" to "b", "c" to "d").
Also, arrays are not maps and vice versa.
This is an example of (a pair of (a string) and (an array of (pairs of strings))).
"a" to arrayOf("b" to "c", "d" to "e")