Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

how to filter list base on another list in kotlin/java?

I have two types FooApi and FooModel:

class FooApi (var aId) 
class FooModel(var mId)

Is a way to simplify below function which filter FooModel list base on FooApi list:

fun f(fooModelList: List<FooModel>, fooApiList: List<FooApi>) : List<FooModel> {
  return fooModelList.filter { fooApiList.map { it.aId }.contains ( it.mId ) }
}
like image 268
LunaVulpo Avatar asked Aug 10 '18 08:08

LunaVulpo


People also ask

How do I filter a list based on another list in Java 8?

One of the utility method filter() helps to filter the stream elements that satisfy the provided criteria. The predicate is a functional interface that takes a single element as an argument and evaluates it against a specified condition.

How do I filter data with Kotlin?

If we want to filter using element index or position, we have to use filterIndexed(). filterIndexed() function takes a predicate with two arguments: index and the value of an element. We can filter the collections by negative conditions by using filterNot().

How do you filter an array of objects in Kotlin?

If you want to use element positions in the filter, use filterIndexed() . It takes a predicate with two arguments: the index and the value of an element. To filter collections by negative conditions, use filterNot() . It returns a list of elements for which the predicate yields false .


2 Answers

It looks ok to me. I would only change some minor things (not required though), so that it ends up something like the following:

 fun List<FooModel>.f(fooApiList: List<FooApi>) = filter { m -> fooApiList.any { it.aId == m.mId } }

Some reasons why I did it this way:

  • I think that filtering is always applied on a list of FooModels, right? (that is the reason for the extension function narrowing the type to List<FooModel>)
  • You are not interested in the mapped object of fooApiList, so that is why I used any instead; the nice benefit there also is that now both values that are compared are next to each other
  • summarizing everything can be said so easily, you can even omit the method body (and therefore return type, return statement, etc.)

Still, that's nearly the same as you did already... Just a bit less code and a rearrangement... Calling it by the way would look like:

val listA : List<FooModel> = TODO()
val listB : List<FooApi> = TODO()

val containedList = listA.f(listB)

If you require such a construct more often, maybe the following more generic solution is helpful:

fun <T, U> List<T>.intersect(uList: List<U>, filterPredicate : (T, U) -> Boolean) = filter { m -> uList.any { filterPredicate(m, it)} }

Which you can then also use like:

val containedList = listA.intersect(listB) {
    a, b -> a.aId == b.mId
}

Then your f again might even look just like:

fun List<FooModel>.f(fooApiList: List<FooApi>) = intersect(fooApiList) { a, b ->  a.mId == b.aId }
like image 91
Roland Avatar answered Oct 24 '22 02:10

Roland


I would do something like

val apiList = listOf(FooApi(1), FooApi(2), FooApi(3))
val modelList = listOf(FooModel(1), FooModel(3))

val output = apiList.flatMap { api -> modelList.filter { api.id == it.id }}

Givin as output

[FooModel(id=1), FooModel(id=3)]

I don't know if the difference is significant...

like image 39
MajorShepard Avatar answered Oct 24 '22 03:10

MajorShepard