Make a new list from two other lists of different types by comparing values of each type - list

I have two Lists of objects that both implement an interface, but are otherwise unrelated. How can I create a new collection of objects containing only the objects of one of the lists that match a value in the other list?
Obviously I could use a for loop & do this manually, but I'd like to know how I can do this using Kotlin's standard library collection filtering functions.
So here's an example:
interface Ids
{
val id: Int
}
data class A(override val id: Int, val name: String) : Ids
data class B(override val id: Int, val timestamp: Long) : Ids
fun main(args: Array<String>) {
val a1 = A(1, "Steve")
val a2 = A(2, "Ed")
val aCol = listOf(a1, a2)
val b2 = B(2, 12345)
val b3 = B(3, 67890)
val bCol = listOf(b2, b3)
val matches = mutableListOf<B>()
// This is where I'm stuck.
// I want to filter bCol using objects from aCol as a filter.
// The result should be that matches contains only a single object: b2
// because bCol[0].id == aCol[1].id
// I'm guessing I need to start with something like this:
bCol.filterTo(matches) { ??? }
}

A straightforward approach would be to search aCol for an object with the same id for each b in bCol:
bCol.filter { b -> aCol.any { a -> a.id == b.id } }
However that may become too slow if your lists are big enough.
To make it more scalable you can first build a set of all ids in aCol:
val aColIds = aCol.map { it.id }.toSet()
And then use Set.contains method to determine whether b.id is in aColIds:
bCol.filter { it.id in aColIds }
// or equivalent
bCol.filter { aColIds.contains(it.id) }

Related

Construct a List<Map> object out of multiple List<Any> objects in Kotlin

I have multiple different data classes in kotlin and they are saved in extra lists with the exact same length. Now I want to combine them into a result list which contains them as a map with the same length (1000 entries). I tried it with a for loop and a prefilled list of maps, but that looks a bit messy to me, so I asked myself if there is a cleaner way of doing this, especially with kotlin.
data class One(
val a: Double,
val b: Double,
var c: Double
)
data class Two(
val d: Double,
val e: Double,
)
fun main() {
val oneList: List<One> = listOf(One(1.0, 0.0, 2.0), One(3.0, 5.0, 10.0))
val twoList: List<Two> = listOf(Two(5.0, 2.0), Two(7.0, 1.0))
val results: List<MutableMap<String,Double>> = (0..1).map{ mutableMapOf() }
for(i in 0 .. 1){
results[i]["a"] = oneList[i].a
results[i]["b"] = oneList[i].b
results[i]["c"] = oneList[i].c
results[i]["d"] = twoList[i].d
results[i]["e"] = twoList[i].e
}
}
The problem is, that I have about 10 classes with around 2-3 members in each object, whereby the code would be about 30+ lines... The result should look like this:
[
{
a: 1.0,
b: 0.0,
c: 2.0,
d: 5.0,
e: 2.0
},
{
a: 3.0,
b: 5.0,
c: 10.0,
d: 7.0,
e: 1.0
}
]
You could use .zip in order to link the 2 lists and then directly create the list using .map and a simple mapOf() to create the map.
val results = oneList.zip(twoList).map { (one, two) ->
mapOf("a" to one.a, "b" to one.b, "c" to one.c, "d" to two.d, "e" to two.e)
}
As for actually doing this for a lot of classes ... not 100% sure. You could use reflexion maybe, but that is not something you use in an actual program usually. Another way would be to create a function in each of the classes that would give a map and then just add those maps together in the .map from above. That way, the code would look a bit cleaner. Something along the lines of:
data class One(
val a: Double,
val b: Double,
var c: Double
){
fun toMap(): Map<String, Double> {
return mapOf("a" to a, "b" to b, "c" to c)
}
}
//.....
val results = oneList.zip(twoList).map { (one, two) -> one.toMap() + two.toMap() }
But you'll soon notice that zip doesn't work with more than 2 lists. I would suggest implementing something like this: Zip multiple lists SO. But that as well won't work, since your classes are of a different type. What I would do is create a abstract class with a toMap() fun, and then all the classes that you need there can inherit it. It would look something like this:
abstract class MotherClass(){
abstract fun toMap(): Map<String, Double>
}
data class One(
val a: Double,
val b: Double,
var c: Double
):MotherClass() {
override fun toMap(): Map<String, Double> {
return mapOf("a" to a, "b" to b, "c" to c)
}
}
// ...
val results = zip(oneList, twoList /*, threeList, etc */).map { list -> list.map { it.toMap() } }
So at the end of the day, you want a String representation of a property name, mapped to its value? I think you have two choices there:
use reflection to fetch all the member names, filter on the types you want (e.g. only the Doubles), so your Strings are derived directly from the class at runtime
define a String somewhere that acts as a label for each property, and try to tie it as closely to the class as you can, so it's easy to maintain. Because it's not derived from the property itself, you'll have to keep the property name and its label in sync - there's no inherent connection between the two things that could automate it
You're already doing the latter in your code - you're using hardcoded arbitrary labels like "a" and "b" when building your results map, and that's where you're connecting a label with a property (e.g. results[i]["a"] = oneList[i].a). So here's another way you could approach that!
Kotlin playground
interface Mappable {
// allows us to declare classes as having the property map we're using
abstract val propertyMap: Map<String, Double>
}
data class One(
val a: Double,
val b: Double,
var c: Double
) : Mappable {
// you have to do this association somewhere - may as well be in the class itself
// you could also use reflection to build this map automatically
override val propertyMap = mapOf("a" to a, "b" to b, "c" to c)
}
data class Two(
val d: Double,
val e: Double,
) : Mappable {
override val propertyMap = mapOf("d" to d, "e" to e)
}
fun main() {
val oneList = listOf(One(1.0, 0.0, 2.0), One(3.0, 5.0, 10.0))
val twoList = listOf(Two(5.0, 2.0), Two(7.0, 1.0))
combine(oneList, twoList).forEach(::println)
}
fun combine(vararg lists: List<Mappable>): List<Map<String, Double>> {
// lists could be different lengths, so we go until one of them runs out
val length = lists.minOf { it.size }
return (0 until length).map { index ->
// create a combined map for each index
mutableMapOf<String, Double>().apply {
// visit each list at this index, grabbing the property map from each object
// and adding its contents to the combined map we're building
lists.map { it.elementAt(index).propertyMap }.forEach(this::putAll)
}
}
}
>> {a=1.0, b=0.0, c=2.0, d=5.0, e=2.0}
{a=3.0, b=5.0, c=10.0, d=7.0, e=1.0}
The problem really is that artificial connection you're introducing between a property, and a label you explicitly define for it - which you have to ensure you maintain, and if you don't, you got bugs. I think that's unavoidable unless you use reflection.
Also if by any chance this data is originally coming from something arbitrary like JSON (where the problem of validation and labelling a property is earlier in the chain) you could take a look at Kotlin's map delegate and see if that's a better fit for this approach
import kotlin.reflect.full.declaredMemberProperties
data class One(val a: Double, val b: Double, var c: Double)
data class Two(val d: Double, val e: Double)
val oneList: List<One> = listOf(One(1.0, 0.0, 2.0), One(3.0, 5.0, 10.0))
val twoList: List<Two> = listOf(Two(5.0, 2.0), Two(7.0, 1.0))
fun compounded(vararg lists: List<Any>): List<Map<String, Double>> {
return lists
.mapIndexed { index, _ ->
lists
.flatMap {
it[index]::class.declaredMemberProperties.map { prop ->
mapOf(prop.name to prop.call(it[index]) as Double)
}
}
.fold(mapOf()) { acc, map ->
mutableMapOf<String, Double>().apply { putAll(acc + map) }
}
}
}
val result = compounded(oneList, twoList)

Kotlin aggregation function

I need to write somehow a function, that aggregates results in a list.
I'm working with an Order dto (java class)
public class Order {
private Long orderId;
private String description;
...
}
I have two APIs, the one that return orders and the other one that returns suborders. So i retrieve all orders and get all suborders in a loop by predefined ids:
// for example i have a predefined list of order id's
List<Long> orderIds = listOf(1L, 2L, 3L, 4L, 5L)
val allOrders = orderIds.map {
// at first i retrieve an order
val order = orderService.getOrderById(it.orderId)
// then i get a list of suborders
val suborders = suborderService.getSubordersByOrderId(it.orderId)
// ?
}
How can i combine order (Order) and suborders (List) to a list of Order and then all the elements of nested list into a single list?
I think flatMap is what you want:
val allOrders: List<Order> = orderIds.flatMap {
val order = orderService.getOrderById(it)
val suborders = suborderService.getSubordersByOrderId(it)
suborders + order
}
It flatten all the items of the returned list into one single list altogether.

Check items from a List of ValueTuple and return results in another List of ValueTuple C#7

Please consider the following list of ValueTuple C#7
static void Main(string[] args)
{
List<(double prices, int matches)> myList = new List<(double, int)>();
myList.Add((100, 10));
myList.Add((100.50 , 12));
myList.Add((101 , 14));
myList.Add((101.50 , 16));
}
What would be a simple way to search for items meeting conditions for "prices" AND "matches" within the List and return the results in another List of ValueTuple.
For instance if I want to return another List of ValueTuples meeting "prices > 101 and matches > 6"
Can you post an example please?
It's easier if you give names to the items :
var myList = new List<(double d,int i)>
{
(100, 10),
(100.50 , 12),
(101 , 14),
(101.50 , 16)
};
var results = myList.Where(x => x.d>101 && x.i>6);
Without the names you'd have to write
var results = myList.Where(x => x.Item1>101 && x.Item2>6);
C# 7.3 added tuple equality but not comparison. You can now write :
var result = myList.Where(d=>d == (101,14));
and
var result = myList.Where(d=>d != (101,14));
But not
var result = myList.Where(d=>d > (101,14));

How to maintain an immutable list when you impact object linked to each other into this list

I'm trying to code the fast Non Dominated Sorting algorithm (NDS) of Deb used in NSGA2 in immutable way using Scala.
But the problem seems more difficult than i think, so i simplify here the problem to make a MWE.
Imagine a population of Seq[A], and each A element is decoratedA with a list which contains pointers to other elements of the population Seq[A].
A function evalA(a:decoratedA) take the list of linkedA it contains, and decrement value of each.
Next i take a subset list decoratedAPopulation of population A, and call evalA on each. I have a problem, because between each iteration on element on this subset list decoratedAPopulation, i need to update my population of A with the new decoratedA and the new updated linkedA it contain ...
More problematic, each element of population need an update of 'linkedA' to replace the linked element if it change ...
Hum as you can see, it seem complicated to maintain all linked list synchronized in this way. I propose another solution bottom, which probably need recursion to return after each EvalA a new Population with element replaced.
How can i do that correctly in an immutable way ?
It's easy to code in a mutable way, but i don't find a good way to do this in an immutable way, do you have a path or an idea to do that ?
object test extends App{
case class A(value:Int) {def decrement()= new A(value - 1)}
case class decoratedA(oneAdecorated:A, listOfLinkedA:Seq[A])
// We start algorithm loop with A element with value = 0
val population = Seq(new A(0), new A(0), new A(8), new A(1))
val decoratedApopulation = Seq(new decoratedA(population(1),Seq(population(2),population(3))),
new decoratedA(population(2),Seq(population(1),population(3))))
def evalA(a:decoratedA) = {
val newListOfLinked = a.listOfLinkedA.map{e => e.decrement()
new decoratedA(a.oneAdecorated,newListOfLinked)}
}
def run()= {
//decoratedApopulation.map{
// ?
//}
}
}
Update 1:
About the input / output of the initial algorithm.
The first part of Deb algorithm (Step 1 to Step 3) analyse a list of Individual, and compute for each A : (a) domination count, the number of A which dominate me (the value attribute of A) (b) a list of A i dominate (listOfLinkedA).
So it return a Population of decoratedA totally initialized, and for the entry of Step 4 (my problem) i take the first non dominated front, cf. the subset of elements of decoratedA with A value = 0.
My problem start here, with a list of decoratedA with A value = 0; and i search the next front into this list by computing each listOfLinkedA of each of this A
At each iteration between step 4 to step 6, i need to compute a new B subset list of decoratedA with A value = 0. For each , i decrementing first the domination count attribute of each element into listOfLinkedA, then i filter to get the element equal to 0. A the end of step 6, B is saved to a list List[Seq[DecoratedA]], then i restart to step 4 with B, and compute a new C, etc.
Something like that in my code, i call explore() for each element of B, with Q equal at the end to new subset of decoratedA with value (fitness here) = 0 :
case class PopulationElement(popElement:Seq[Double]){
implicit def poptodouble():Seq[Double] = {
popElement
}
}
class SolutionElement(values: PopulationElement, fitness:Double, dominates: Seq[SolutionElement]) {
def decrement()= if (fitness == 0) this else new SolutionElement(values,fitness - 1, dominates)
def explore(Q:Seq[SolutionElement]):(SolutionElement, Seq[SolutionElement])={
// return all dominates elements with fitness - 1
val newSolutionSet = dominates.map{_.decrement()}
val filteredSolution:Seq[SolutionElement] = newSolutionSet.filter{s => s.fitness == 0.0}.diff{Q}
filteredSolution
}
}
A the end of algorithm, i have a final list of seq of decoratedA List[Seq[DecoratedA]] which contain all my fronts computed.
Update 2
A sample of value extracted from this example.
I take only the pareto front (red) and the {f,h,l} next front with dominated count = 1.
case class p(x: Int, y: Int)
val a = A(p(3.5, 1.0),0)
val b = A(p(3.0, 1.5),0)
val c = A(p(2.0, 2.0),0)
val d = A(p(1.0, 3.0),0)
val e = A(p(0.5, 4.0),0)
val f = A(p(0.5, 4.5),1)
val h = A(p(1.5, 3.5),1)
val l = A(p(4.5, 1.0),1)
case class A(XY:p, value:Int) {def decrement()= new A(XY, value - 1)}
case class ARoot(node:A, children:Seq[A])
val population = Seq(
ARoot(a,Seq(f,h,l),
ARoot(b,Seq(f,h,l)),
ARoot(c,Seq(f,h,l)),
ARoot(d,Seq(f,h,l)),
ARoot(e,Seq(f,h,l)),
ARoot(f,Nil),
ARoot(h,Nil),
ARoot(l,Nil))
Algorithm return List(List(a,b,c,d,e), List(f,h,l))
Update 3
After 2 hour, and some pattern matching problems (Ahum...) i'm comming back with complete example which compute automaticaly the dominated counter, and the children of each ARoot.
But i have the same problem, my children list computation is not totally correct, because each element A is possibly a shared member of another ARoot children list, so i need to think about your answer to modify it :/ At this time i only compute children list of Seq[p], and i need list of seq[A]
case class p(x: Double, y: Double){
def toSeq():Seq[Double] = Seq(x,y)
}
case class A(XY:p, dominatedCounter:Int) {def decrement()= new A(XY, dominatedCounter - 1)}
case class ARoot(node:A, children:Seq[A])
case class ARootRaw(node:A, children:Seq[p])
object test_stackoverflow extends App {
val a = new p(3.5, 1.0)
val b = new p(3.0, 1.5)
val c = new p(2.0, 2.0)
val d = new p(1.0, 3.0)
val e = new p(0.5, 4.0)
val f = new p(0.5, 4.5)
val g = new p(1.5, 4.5)
val h = new p(1.5, 3.5)
val i = new p(2.0, 3.5)
val j = new p(2.5, 3.0)
val k = new p(3.5, 2.0)
val l = new p(4.5, 1.0)
val m = new p(4.5, 2.5)
val n = new p(4.0, 4.0)
val o = new p(3.0, 4.0)
val p = new p(5.0, 4.5)
def isStriclyDominated(p1: p, p2: p): Boolean = {
(p1.toSeq zip p2.toSeq).exists { case (g1, g2) => g1 < g2 }
}
def sortedByRank(population: Seq[p]) = {
def paretoRanking(values: Set[p]) = {
//comment from #dk14: I suppose order of values isn't matter here, otherwise use SortedSet
values.map { v1 =>
val t = (values - v1).filter(isStriclyDominated(v1, _)).toSeq
val a = new A(v1, values.size - t.size - 1)
val root = new ARootRaw(a, t)
println("Root value ", root)
root
}
}
val listOfARootRaw = paretoRanking(population.toSet)
//From #dk14: Here is convertion from Seq[p] to Seq[A]
val dominations: Map[p, Int] = listOfARootRaw.map(a => a.node.XY -> a.node.dominatedCounter) //From #dk14: It's a map with dominatedCounter for each point
val listOfARoot = listOfARootRaw.map(raw => ARoot(raw.node, raw.children.map(p => A(p, dominations.getOrElse(p, 0)))))
listOfARoot.groupBy(_.node.dominatedCounter)
}
//Get the first front, a subset of ARoot, and start the step 4
println(sortedByRank(Seq(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p)).head)
}
Talking about your problem with distinguishing fronts (after update 2):
val (left,right) = population.partition(_.node.value == 0)
List(left, right.map(_.copy(node = node.copy(value = node.value - 1))))
No need for mutating anything here. copy will copy everything but fields you specified with new values. Talking about the code, the new copy will be linked to the same list of children, but new value = value - 1.
P.S. I have a feeling you may actually want to do something like this:
case class A(id: String, level: Int)
val a = A("a", 1)
val b = A("b", 2)
val c = A("c", 2)
val d = A("d", 3)
clusterize(List(a,b,c,d)) === List(List(a), List(b,c), List(d))
It's simple to implement:
def clusterize(list: List[A]) =
list.groupBy(_.level).toList.sortBy(_._1).map(_._2)
Test:
scala> clusterize(List(A("a", 1), A("b", 2), A("c", 2), A("d", 3)))
res2: List[List[A]] = List(List(A(a,1)), List(A(b,2), A(c,2)), List(A(d,3)))
P.S.2. Please consider better naming conventions, like here.
Talking about "mutating" elements in some complex structure:
The idea of "immutable mutating" some shared (between parts of a structure) value is to separate your "mutation" from the structure. Or simply saying, divide and conquerror:
calculate changes in advance
apply them
The code:
case class A(v: Int)
case class AA(a: A, seq: Seq[A]) //decoratedA
def update(input: Seq[AA]) = {
//shows how to decrement each value wherever it is:
val stats = input.map(_.a).groupBy(identity).mapValues(_.size) //domination count for each A
def upd(a: A) = A(a.v - stats.getOrElse(a, 0)) //apply decrement
input.map(aa => aa.copy(aa = aa.seq.map(upd))) //traverse and "update" original structure
}
So, I've introduced new Map[A, Int] structure, that shows how to modify the original one. This approach is based on highly simplified version of Applicative Functor concept. In general case, it should be Map[A, A => A] or even Map[K, A => B] or even Map[K, Zipper[A] => B] as applicative functor (input <*> map). *Zipper (see 1, 2) actually could give you information about current element's context.
Notes:
I assumed that As with same value are same; that's default behaviour for case classess, otherwise you need to provide some additional id's (or redefine hashCode/equals).
If you need more levels - like AA(AA(AA(...)))) - just make stats and upd recursive, if dеcrement's weight depends on nesting level - just add nesting level as parameter to your recursive function.
If decrement depends on parent node (like decrement only A(3)'s, which belongs to A(3)) - add parent node(s) as part of stats's key and analise it during upd.
If there is some dependency between stats calculation (how much to decrement) of let's say input(1) from input(0) - you should use foldLeft with partial stats as accumulator: val stats = input.foldLeft(Map[A, Int]())((partialStats, elem) => partialStats ++ analize(partialStats, elem))
Btw, it takes O(N) here (linear memory and cpu usage)
Example:
scala> val population = Seq(A(3), A(6), A(8), A(3))
population: Seq[A] = List(A(3), A(6), A(8), A(3))
scala> val input = Seq(AA(population(1),Seq(population(2),population(3))), AA(population(2),Seq(population(1),population(3))))
input: Seq[AA] = List(AA(A(6),List(A(8), A(3))), AA(A(8),List(A(6), A(3))))
scala> update(input)
res34: Seq[AA] = List(AA(A(5),List(A(7), A(3))), AA(A(7),List(A(5), A(3))))

How to convert list of list to simple list by removing duplicate values using scala?

I have following list -
List(List(
List(((groupName,group1),(tagMember,["192.168.20.30","192.168.20.20","192.168.20.21"]))),
List(((groupName,group1),(tagMember,["192.168.20.30"]))),
List(((groupName,group1),(tagMember,["192.168.20.30","192.168.20.20"])))))
I want to convert it to -
List((groupName, group1),(tagMember,["192.168.20.30","192.168.20.20","192.168.20.21"]))
I tried to use .flatten but unable to form desired output.
How do I get above mentioned output using scala??
I had to make some changes to your input to make it valid.
Input List:
val ll = List(List(
List((("groupName","group1"),("tagMember", List("192.168.20.30","192.168.20.20","192.168.20.21")))),
List((("groupName","group1"),("tagMember",List("192.168.20.30")))),
List((("groupName","group1"),("tagMember",List("192.168.20.30","192.168.20.20"))))
))
Code below works if the group, and tagMember are the same across all the elements in the list
def getUniqueIpsConstantGroupTagMember(inputList: List[List[List[((String, String), (String, List[String]))]]]) = {
// List[((String, String), (String, List[String]))]
val flattenedList = ll.flatten.flatten
if (flattenedList.size > 0) {
val group = flattenedList(0)._1
val tagMember = flattenedList(0)._2._1
val ips = flattenedList flatMap (_._2._2)
((group), (tagMember, ips.distinct))
}
else List()
}
println(getUniqueIpsConstantGroupTagMember(ll))
Output:
((groupName,group1),(tagMember,List(192.168.20.30, 192.168.20.20, 192.168.20.21)))
Now, let's assume you could have different groupNames.
Sample input:
val listWithVariableGroups = List(List(
List((("groupName","group1"),("tagMember",List("192.168.20.30","192.168.20.20","192.168.20.21")))),
List((("groupName","group1"),("tagMember",List("192.168.20.30")))),
List((("groupName","group1"),("tagMember",List("192.168.20.30","192.168.20.20")))),
List((("groupName","group2"),("tagMember",List("192.168.20.30","192.168.20.10"))))
))
The following code should work.
def getUniqueIpsForMultipleGroups(inputList: List[List[List[((String, String), (String, List[String]))]]]) = {
val flattenedList = inputList.flatten.flatten
// Map[(String, String),List[(String, List[String])]]
val groupedByGroupNameId = flattenedList.groupBy(p => p._1) map {
case (key, value) => (key, ("tagMember", extractUniqueTagIps(value)))
}
groupedByGroupNameId
}
def extractUniqueTagIps(list: List[((String, String), (String, List[String]))]) = {
val ips = list flatMap (_._2._2)
ips.distinct
}
getUniqueIpsForMultipleGroups(listWithVariableGroups).foreach(println)
Output:
((groupName,group1),(tagMember,List(192.168.20.30, 192.168.20.20, 192.168.20.21)))
((groupName,group2),(tagMember,List(192.168.20.30, 192.168.20.10)))