I have two lists of strings in kotlin, how to concatenate each element of the first list with elements of the second one respective to the positions? - kotlin

I have two lists:
val a = listOf("a", "b", "c")
val b = listOf("1", "2", "3")
I want to get a list that looks like this: ["a1", "b2", "c3"].
How to do this in the most efficient way?

The easiest way would look like this:
val c = a.zip(b).map {it.first + it.second }

Related

Kotlin - Filter list with values from another list

This is probably super simple but I just cannot figure out how to google for that.
What I have is:
data class Post(val id: String)
val ids = listOf("1", "5", "19")
val posts = listOf<Post>(post1, post2, post3 etc)
now I want to filter posts list with the ids list.
This is how I filter one id:
val output = posts.filter{ it.id == ids[0]}
but how do I filter for all the items in "ids" list?
You can use a small modification of the code you wrote to filter out a single Post by checking if ids contains the id of a Post instead of comparing it only to the first value in ids:
fun main() {
// list of ids to be filtered
val ids = listOf("1", "5", "19")
// list of posts to filter from
val posts = listOf(
Post("1"), Post("2"),
Post("3"), Post("5"),
Post("9"), Post("10"),
Post("15"), Post("19"),
Post("20")
)
// filter a single post that matches the first id from ids
val singleOutput = posts.filter { it.id == ids[0] }
// filter all posts that have an id contained in ids
val multiOutput = posts.filter { ids.contains(it.id) }
// print the single post with the matching id
println(singleOutput)
// print the list of posts with matching ids
println(multiOutput)
}
The output of this is
[Post(id=1)]
[Post(id=1), Post(id=5), Post(id=19)]
You just have to use 'any' in your filter function to compare all your list elements.
val output = posts.filter { post -> ids.any { id -> id == post.id } }

how to swap places of strings in a kotlin list?

i have a list in my project like
val list1 = listOf("1", "pig", "3", "cow")
and i need to swap it to like "pig", "1", "cow", "3"
and the number and words will be random so it cant be only on these words
can anyone tell me how to do this?
You can combine chunked() to get pairs of items and then flatMap() to swap them and recreate a flat list:
list1
.chunked(2)
.flatMap { listOf(it[1], it[0]) } // or: it.reversed()
However, it looks pretty weird that you have a list like this in the first place. If items of this list are stored in two subsequent indexes, then such design complicates maintaining and processing of the data. Instead, create a data class for both fields and create a list of such data items:
val list = listOf(Animal("1", "pig"), Animal("3", "cow"))
data class Animal(
val id: String,
val name: String,
)

Using RxJava to generate a map where keys are values of a Kotlin enum and map's values come from another RxJava stream

Introduction
Let's say I have a Kotlin enum class:
enum class Type {
ONE,
TWO,
THREE,
FOUR
}
and a following data class:
data class Item(
val name: String,
val type: Type
)
Then I have a Single that emits a list of Items – can by anything but for example purposes, let's say it looks like that:
val itemsSingle = Single.just(listOf(
Item("A", Type.ONE),
Item("B", Type.ONE),
Item("C", Type.TWO),
Item("D", Type.THREE),
))
Problem
What I'd like to achieve is to have an RxJava stream that will output a map where keys come from Type and values are lists of Items matching a given Type value (where an undetermined, unsorted list of Items is provided by some other Single stream). The signature would be:
Single<Map<Type, List<Item>> // or Observable<Map<Type, List<Item>>
One additional requirement is that the map's keys should always exhaust all values from Type enum even if the itemsSingle stream contains no items for some Type values (or no items at all). So, for the provided itemsSingle example stream the returned map should look like this:
{
ONE: [ Item(name: "A", type: ONE), Item(name: "B", type: ONE) ],
TWO: [ Item(name: "C", type: TWO) ],
THREE: [ Item(name: "D", type: THREE) ],
FOUR: []
}
Attempt
With all the above, I've kinda achieved the desired result with following steps:
To satisfy the requirement of exhausting all Type enum values I first create a map that has an empty list for all possible Type values:
val typeValuesMap = Type.values().associate { it to emptyList<Item>() }
val typeValuesMapSingle = Single.just(typeValuesMap)
// result: {ONE=[], TWO=[], THREE=[], FOUR=[]}
I can get a map that contains items from itemsSingle grouped under respective Type value keys:
val groupedItemsMapSingle = itemsSingle.flattenAsObservable { it }
.groupBy { it.type }
.flatMapSingle { it.toList() }
.toMap { list -> list[0].type } // the list is guaranteed to have at least one item
// result: {ONE=[Item(name=A, type=ONE), Item(name=B, type=ONE)], THREE=[Item(name=D, type=THREE)], TWO=[Item(name=C, type=TWO)]}
finally I can combine both lists using the combineLatest operator and overwriting initial empty list of items for a given Type value if itemsSingle contained any Items for this Type value:
Observable.combineLatest(
typeValuesMapSingle.flattenAsObservable { it.entries },
groupedItemsMapSingle.flattenAsObservable { it.entries }
) { a, b -> listOf(a, b) }
.defaultIfEmpty(typeValuesMap.entries.toList()) // in case itemsSingle is empty
.flatMapIterable { it }
.collect({mutableMapOf<Type, List<Item>>()}, { a, b -> a[b.key] = b.value})
// result: {FOUR=[], ONE=[Item(name=A, type=ONE), Item(name=B, type=ONE)], THREE=[Item(name=D, type=THREE)], TWO=[Item(name=C, type=TWO)]}
Summary
As you can see, it's quite a lot of code for a seemingly simple operation. So my question is – is there a simpler way to achieve the result I'm after?
Just merge a map of empty lists with a map of filled lists
val result = itemsSingle.map { items->
Type.values().associateWith { listOf<Item>() } + items.groupBy { it.type }
}

Kotlin: functional way to split list into grouped lists [duplicate]

This question already has answers here:
How to split a list into sublists using a predicate with Kotlin?
(2 answers)
Closed 1 year ago.
I want to group specific items in a list based on a 'split predicate'. An example of what I mean:
val list = listOf("1", "2", "3", "", "4", "5", "6", "", "7")
// looking for: a nice readable (maybe functional?) way to get to:
val result = listOf(
listOf("1", "2", "3"),
listOf("4", "5", "6"),
listOf("7"),
)
As you see, I want to split the list on items which are item.isBlank() - which I also want to drop in the process.
I know how to do it imperatively, but I think there must be a nice, readable, functional way!
Thanks a lot & regards
Marc
I had a solution that is similar to Matt's (had to run to a meeting and couldn't post it earlier!). It is materially the same, but allows you to specify an arbitrary predicate to split on, accounts for the fact that you might start with a blank item and end up with an unwanted empty list (or if you have two items that match the predicate in a row), and is defined as an extension function:
fun <T> List<T>.split(predicate: (T) -> Boolean): List<List<T>> =
fold(mutableListOf(mutableListOf<T>())) { acc, t ->
if (predicate(t)) acc.add(mutableListOf())
else acc.last().add(t)
acc
}.filterNot { it.isEmpty() }
And to call it:
list.split { it.isBlank() }
// Returns [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
There's probably a cleaner way but I think this solves it
list.fold (mutableListOf(mutableListOf<String>())) { acc, item ->
if (item.isBlank()) {
acc.add(mutableListOf())
} else {
acc.last().add(item)
}
acc
}

Kotlin how to split a list in to sublists

I would like to split a list into a few sublists, but I have no idea how to do it.
Once of my ideas was splitting the list by index of element. For exmaple "B" index is 0, "S" index 2, so I would like to take a part between index 0 - 1 into the first sublist, then the second sublist should be the part between index 2 - 5.
Example of my list:
val listOfObj = listOf("B", "B" , "S", "B", "B", "X", "S", "B", "B", "P")
Result after splitting:
listOf(listOf("B","B"), listOf("S", "B", "B", "X"), listOf("S", "B", "B", "P") )
How do I achieve such a result?
Here it goes. I wrote it from my phone without checking but the idea is basic.
val result = mutableListOf<List<String>>()
var current = mutableList<String>()
listOfObj.forEach { letter ->
if (letter == "S") {
result.add(current)
current = mutableListOf<String>()
}
current.add(letter)
}
if (current.isNotEmpty()) {
result.add(current)
}
You can even create an extension function for a List<T> that gets a separator element as a parameter and returns a list of lists.