merge every two equal elements into another one - kotlin

How to merge every two equal elements in the list into one element and the merge process is specified by lambda function.
For example if I have a list like this
["a","a","b","b"]
And the merge process is {it.repeat(2)}
it should be like this:
["aa","bb"]
but if the list is like this:
["a","a","a"]
It should be converted to
["aa","a"]
Another example is :
["a","b","a","b"]
will be
["a", "b","a","b"]
I tried zipWithNext() but I can't figure out what to do next especially when there is more than two repeat of the same letter.
How to implement like this in Kotlin using functional programming way of Kotlin.

Here comes the very inefficient but, desired solution. As requested, I've used functional programming as much as possible. Hope you'll like it. But, it would be very easy if you use loop or regular expression to solve this problem.
In this question functional style is not needed.
fun main(args: Array<String>) {
val list = listOf("a", "a", "a", "a", "b", "b", "b", "b")
//val list = listOf("a", "a", "a", "b", "a")
var temp = false
val result = list.zipWithNext().map {
if (temp) {
temp = false
null
} else {
if (it.first == it.second) {
temp = true
it.first + it.second
} else {
it.first
}
}
}.filter { !it.isNullOrBlank() }.toMutableList()
if (!temp) {
result.add(list.last())
}
print(result)
}

Related

Kotlin to filter certain options using When

How can I use When in Kotlin to do something if not in some certain conditions? for example
when(condition : String){
//not "A","B","C" -> return
// else -> continue to do something
}
I've tried some keyword or operator such as ! or not, but doesn't
work
For the specific case in your question, here are two ways I would do it:
when (myString) {
!in arrayOf("A", "B", "C") -> return
else -> {
// continue to do something
}
}
Just flipping the condition (swap the order of your code):
when (myString) {
"A", "B", "C" -> {
// continue to do something
}
else -> { }
}
if not in some certain conditions?
Exactly that.
let's suppose you have this :
val value = 5
to check if something is within a certain range of values:
when (value in 0..5) {
true -> {}
else -> {}
}
alternatively:
when (value !in 0..5) {
true -> {}
else -> {}
}
but making use of negative logic is sometimes harder to read or follow, so the first approach would probably be easier to understand perhaps.
similary for strings:
val range = arrayListOf("a", "b", "c")
when (value in range) {
true -> {}
else -> {}
}
you could also expand this further:
val value = "hello"
val allowedRange = arrayListOf("a", "b", "c")
val excludedRange = arrayListOf("x", "y", "z")
when (value) {
in allowedRange -> {
}
in excludedRange -> {
}
else -> {}
}

Kotlin: How to flatten list of Hashmaps

How can I flatten a list of HashMaps in Kotlin?
var listOfMaps: List<Map<String, String>> = listOf(mapOf("test" to "test1"), mapOf("test2" to "test3"), mapOf("test4" to "test5"))
I would like to get:Map<String,String> with all key value paires
val map:Map<String, String> = listOfMaps
.flatMap { it.entries }
.associate { it.key to it.value }
you can do something like this if you don't know if the list can be empty.
val map = listOfMaps.fold(mapOf<String, String>()) {acc, value -> acc + value }
If the list never will be empty you can use reduce instead.
Thank you Demigod for the comments
You could use fold:
listOfMaps.fold(
mutableMapOf<String, String>(),
{ acc, item -> acc.also { it.putAll(item) } }
)
The first parameter mutableMapOf<String, String>() creates an empty map to put the values into. This is called the accumulator
The second parameter is a function which takes two arguments
The accumulator
An item from the original list
This function is run sequentially against all items in the list. In our case it adds all the items from each map to the accumulator.
Note: This function does not account for duplicate keys. If a later map has the same key as an earlier one then the value just gets overridden.
Also note (pun intended): We use acc.also {} as we want to return the actual map, not the return value from the addAll method
Well... seeing lots of solutions, I will add my two cents here:
If you don't mind losing the values of duplicated keys you can use something as follows:
listOfMaps.flatMap { it.entries }.associate{ it.key to it.value } // or: it.toPair() if you will
// depending on how large those lists can become, you may want to consider also using asSequence
If you instead want to collect all entries including duplicate keys (i.e. saving all the values), use the following instead (which then gives you a Map<String, List<String>>):
listOfMaps.flatMap { it.entries }.groupBy({ it.key }) { it.value }
Also here the comment regarding asSequence holds...
Finally if you can omit those maps within the list and just use a Pair instead, that will spare you the flatMap { it.entries }-call and make things even easier, e.g. you could just call .toMap() then for the first case and groupBy directly for the second and the question regarding asSequence no longer arises.
An extra addition to this, if you have single value maps, maybe you want to switch to a List<Prair<String, String>>. In that case, the solution is straight forward:
You would have something like:
var listOfMaps: List<Pair<String, String>> = listOf("test" to "test1", "test2" to "test3", "test4" to "test5")
and toMap() would dsolve it all:
listOfMaps.toMap()
If you don't have duplicate keys or don't care for them, you can do it like this:
val flattenedMap = listOfMaps.flatMap { it.toList() }.toMap()
If you have duplicate keys and care for them, you can do it like this:
val flattenedMap = mutableMapOf<String, MutableList<String>>().apply {
listOfMaps.flatMap { it.toList() }.forEach {
getOrPut(it.first) {
mutableListOf()
}.add(it.second)
}
}
The result will be Map<String, List<String>> then of course.
This solution works with same keys (and different collection as values) by merging them together instead of overwriting them
/**
* Merge two maps with lists
*/
fun <K,V>Map<K,Collection<V>>.mergeLists(other: Map<K,Collection<V>>) =
(this.keys + other.keys).associateWith {key ->
setOf(this[key], other[key]).filterNotNull()
}.mapValues { (_,b) -> b.flatten().distinct() }
/**
* Merge two maps with sets
*/
fun <K,V>Map<K,Collection<V>>.mergeSets(other: Map<K,Collection<V>>) =
(this.keys + other.keys).associateWith {key ->
setOf(this[key], other[key]).filterNotNull()
}.mapValues { (_,b) -> b.flatten().toSet() }
Then use like e.g. listOfMaps.reduce { a, b -> a.mergeSets(b) }
Test:
#Test
fun `should merge two maps with as lists or sets`() {
// GIVEN
val map1 = mapOf(
"a" to listOf(1, 2, 3),
"b" to listOf(4, 5, 6),
"c" to listOf(10),
"e" to emptyList()
)
val map2 = mapOf(
"a" to listOf(1, 9),
"b" to listOf(7),
"d" to listOf(null)
)
// WHEN
val mergedAsLists = map1.mergeLists(map2)
val mergedAsSets = map1.mergeSets(map2)
// THEN
listOf(mergedAsLists, mergedAsSets).forEach { merged ->
assertThat(merged.keys).containsOnly("a", "b", "c", "d", "e")
assertThat(merged["a"]).containsOnly(1,2,3,9)
assertThat(merged["b"]).containsOnly(4,5,6,7)
assertThat(merged["c"]).containsOnly(10)
assertThat(merged["d"]).containsOnly(null)
assertThat(merged["e"]).isEmpty()
}
}

Kotlin recursive function to compare two array and keep the content of origin array

I need to compare two MutableList, requiredDevices and providedDevices using recursive function. These two MutableList is the element of a MutableList, originalArray as well. If there is a match, it will take out the matched element of requiredDevices and providedDevices, one by one, and add the leftover to list1 and list2, respectively. How can I do this and keep the content of requiredDevices and providedDevices and originalArray . For example:
originalArray=[requiredDevices, providedDevices]
=[["A","B","C"],["B,"C","A","A","D"]]
requiredDevices=["A","B","C"]
providedDevices=["B,"C","A","A","D"]
, after the recursion:
list1=[] (empty array)
list2=["A","D"]
originalArray=[[],["A","D"]]
Tried to write a recursive function and take requiredDevices and providedDevices as argument. The recursive function returns a list temp contains list1 and list2. But requiredDevices and providedDevices is modified as well.
val temp = mutableListOf<MutableList<String>>()
fun compareArray(requiredDevices: MutableList<String>, providedDevices: MutableList<String>): List<MutableList<String>> {
for (i in 1 until requiredDevices.size) {
for (j in 0 until providedDevices.size) {
try {
if (requiredDevices[i] == providedDevices[j]) {
requiredDevices.removeAt(i)
providedDevices.removeAt(j)
compareArray(requiredDevices, providedDevices)
temp.add(requiredDevices)
temp.add(providedDevices)
}
} catch (e: IndexOutOfBoundsException) {
}
}
}
return temp.distinct()
}
I want to keep the originalArray content and still get the same result of list1 and list2. As following:
originalArray=[["A","B","C"],["B,"C","A","D"]]
I don't really follow what your algorithm is trying to do. Based on your comment, I think this solves the problem. This copies the two starting lists into new lists, and then removes items from the copies one-by-one, so they will retain excess items if they have them.
fun <T> removeCommonValues(listA: List<T>, listB: List<T>): Pair<List<T>, List<T>> {
val outA = listA.toMutableList()
val outB = listB.toMutableList()
for (i in (outA.size - 1) downTo 0){
for (j in (outB.size - 1) downTo 0){
if (outA[i] == outB[j]){
outA.removeAt(i)
outB.removeAt(j)
break
}
}
}
return outA to outB
}
Test:
fun main() {
val required = listOf("A", "B", "C", "C")
val available = listOf("B", "C", "A", "D", "D")
println(removeCommonValues(required, available))
}
// Outputs ([C], [D, D])

Kotlin FP : Convert List<String> to Map<String,Int>

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)

Mapping an iterable 1-to-n in kotlin

What I am trying to do is use the Iterable.map but instead of transforming every one value to one new value, I want to transform one value to multiple new values.
For example:
val myList = listOf("test", "123", "another.test", "test2")
val result = myList.map {
if(it.contains(".")) {
return#map it.split(".")
} else {
return#map it
}
}
//desired output: ["test", "123", "another", "test", "test2"]
This code would result in a List which contains both strings and lists of strings (type Any).
How could I most elegantly implement this?
One quick way to do this is using flatMap.
val output = myList.flatMap { if(it.contains(".")) it.split(".") else listOf(it) }
The flatMap method transforms the each element using given function and then flatten the result to a single list.
https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/flat-map.html