Kotlin - Filter list with values from another list - kotlin

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 } }

Related

How do I take the latest item given an ID in a list in Kotlin?

Say I have objects that look like this
data class Test(val id: Int, val timeStamp: Long)
So a data set might look like
val data = listOf(
Test(1, 100),
Test(1, 150),
Test(2, 50),
Test(2, 150),
Test(2, 300),
)
I want to be able to filter the list so that I get the latest timestamp of each ID, so my expected output is
data = listOf(
Test(1, 150),
Test(2, 300),
)
Was looking at using groupBy, but was not quite sure where to go from there. The list given is sorted ascending.
You can do it using groupBy and maxBy like this:
data.groupBy { it.id }.values.map { it.maxBy(Test::timeStamp) }
In case of your data is sorted, you can use last instead of maxBy:
data.groupBy { it.id }.values.map { it.last() }
Another way is associateBy, it automatically gets the last element for you:
data.associateBy { it.id }.values.toList()
I think this is one way. I couldn't find any way combining grouping and maxBy. So created unique value set. Then loop and filter the main list with id and get max of that filtered list and add to result.
val result = arrayListOf<Test>()
data.map { it.id }.toSet().forEach {id->
data.filter {
it.id == id
}.maxBy {
it.timeStamp
}?.let {
result.add(it)
}
}

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 }
}

How to add all the values of Data Class field into another list

I am downloading a JSON Object and storing it in a data class. I am able to download the data and successfully and store it in the data class. I want to store all the values inside the option field (which is an array) in the JSON object into a separate ArrayList.
JsonSchema
{8 items
"area":"sat"
"author":"twinword inc."
"email":"help#twinword.com"
"level":3
"quizlist":[10 items
0:{3 items
"correct":2
"option":[2 items
0:"jury"
1:"assess"
]
"quiz":[3 items
0:"value"
1:"estimate"
2:"evaluate"
]
}
1:{...}3 items
2:{...}3 items
3:{...}3 items
4:{...}3 items
5:{...}3 items
6:{...}3 items
7:{...}3 items
8:{...}3 items
9:{...}3 items
]
"result_code":"200"
"result_msg":"Success"
"version":"4.0.0"
}
DataClasses
data class Game2Model(#SerializedName("area") var area : String,
#SerializedName("level") var level : Int,
#SerializedName("quizlist") var quizlist : List<QuizData>,
#SerializedName("version") var version : String,
#SerializedName("author") var author : String,
#SerializedName("email") var email : String,
#SerializedName("result_code") var resultCode : String,
#SerializedName("result_msg") var resultMsg : String ){}
data class QuizData( #SerializedName("quiz") var quiz : List<String>,
#SerializedName("option") var option : List<String>,
#SerializedName("correct") var correct : Int){}
If you need a list of all options in a single bigger list, you could do the following:
val allOptions = game2Model.quizlist.flatMap { q -> q.option }

How to group objects by values

I have an object that looks like this:
data class Product(val name: String,
val maker: List<String>)
Currently, the response that I receive from the backend (and it can't be changed) is as follows:
[{"name":"Car", "maker":["Audi"]},
{"name":"Car", "maker":["BMW"]},
{"name":"Motorcycle", "maker":["Yamaha"]},
{"name":"Motorcycle", "maker":["Kawasaki"]}
]
The actual list consists of a lot of data, but the name field can be trusted to be grouped by.
What would be a way for me to map this data so that the end result is something like this:
[{"name":"Car", "maker":["Audi", "BMW"]},
{"name":"Motorcycle", "maker":["Yamaha","Kawasaki"]}
]
Just use groupBy { ... } and then process the groups map entries, replacing them with a single Product:
val result = products.groupBy { it.name }.entries.map { (name, group) ->
Product(name, group.flatMap { it.maker })
}