GroupBy using two conditions in scala - scala

I have List[(Int,Int)]
For ex.
val a= List((1,2), (3,4), (1,3), (4,2), (5,4), (3,8))
I want to perform operation like this:
Take first element and groupby using below condition:
If first tuple of the element is in remaining elements' first tuple then include it
or
If first tuple of the element is in remaining elements' first tuple then include it
Then skip that tuples which are included and for remaining tuple do the same process.
Possible Answer:
val ans= Map((1,2)->List((1,2),(1,3),(4,2)), (3,4)->List(3,4),(5,4),(3,8))
How can I do this?

This seems to work
a.foldLeft(List[((Int, Int), List[(Int, Int)])]())
{(acc, t) => if (acc.exists (_._2.contains(t)))
acc
else
(t, a.filter(u => u != t && (u._1 == t._1 || u._2 == t._2)))::acc
}.toMap
//> res0: scala.collection.immutable.Map[(Int, Int),List[(Int, Int)]] =
// Map((3,4) -> List((5,4), (3,8)),
(1,2) -> List((1,3), (4,2)))
Go over the list. If this tuple is already in our accumulator, do nothing. Otherwise, filter the list for all tuples that are not the current tuple and share either the first or second element with the current one.

Related

updating List of tuples

I am new to Scala. I have a list of tuples. I want to be able to change the last element of that list to (x, y + 1) and then map every other element to the next one.
val l1: List[(Int, Int)] = List((0,0), (0,1), (0,2), (0,3))
So I need list l2, which after mapping should contain elements (0,1), (0,2), (0,3), (0,4)
Any help would be appreciated
Analog Andronicust answer I prefer this style:
l1.map{ case (x, y) => (x, y + 1) }
So you can Pattern Match on the pairs and give the elements proper names.
In contrast to t._1 etc.
You can map the list like this:
l1.map(t => (t._1, t._2 + 1))
This maps every tuple in the list into tuple with first element unchanged and second incremented by one.
Result:
List((0,1), (0,2), (0,3), (0,4))
Parameter Untupling from Scala 3 might give the following syntax where we no longer have to use pattern matching or ._1 syntax
l.map((x, y) => (x, y + 1))

value map isn't a member of int

so I have the following simple code.
What I want to do is generate for a given list of tuples of (char , int)
another list as explained in the example:
For List((a,2),(b,1)), I want to have a list List( List((a,1)) ,List((a,2)), List((b,1))) .
val abba = List(('a', 2), ('b', 2))
abba.map(elt=> for(t<-elt._2) yield (elt,t))
I tested my approach on the following snippet of code but I got the following error:
Error:(72, 31) value map is not a member of Int
abba.map(elt=> for(t<-elt._2) yield (elt,t))
Any hints on how to solve this problem?
If I interpret your requirement correctly:
For List((a,2),(b,1)), I want to have a list List( List((a,1))
,List((a,2)), List((b,1)))
you can generate your list-of-list using flatMap and for/yield with input 1 to elt._2:
val abba = List(('a', 2), ('b', 1))
abba.flatMap( elt => for(t <- 1 to elt._2) yield List((elt._1, t)) )
// res1: List[List[(Char, Int)]] = List(List((a,1)), List((a,2)), List((b,1)))
Or, you can use case partial function as below:
abba.flatMap{ case (c, i) => for (j <- 1 to i) yield List((c, j)) }
// res2: List[List[(Char, Int)]] = List(List((a,1)), List((a,2)), List((b,1)))
for(t<-elt._2) yield (elt,t) is equivalent to elt._2.map(t => (elt, t)). elt._2 is the second element of the current tuple, so it's an integer. Integers do not have a map method, so the above can't work.
Looking at your expected output, it looks like you want to iterate t from 1 to elt._2. You can do that using the to method:
for(t <- 1 to elt._2) yield (elt._1, t)
It also looks like you want the first element of the original tuple to be the first element of the new tuples, so I added a ._1 as well.
As a style note, it might be a bit cleaner to use pattern matching on the tuple instead of ._1 and ._2.

Scala - Sort a Map based on Tuple Values

I am attempting to sort a Map based on Value Tuples
val data = Map("ip-11-254-25-225:9000" -> (1, 1413669308124L),
"ip-11-232-145-172:9000" -> (0, 1413669141265L),
"ip-11-232-132-31:9000" -> (0, 1413669128111L),
"ip-11-253-67-184:9000" -> (0, 1413669134073L),
"ip-11-232-142-77:9000" -> (0, 1413669139043L))
The criteria of sort should be based on both the values in the tuple
I tried
SortedMap[String, (Long,Long)]() ++ data
but with no success.
Can someone please suggest a better way to sort first on tuple._1 and then on tuple._2
In general you can select which element in the tuple has priority in the sorting; consider this
data.toSeq.sortBy {case (k,(a,b)) => (k,a,b) }
In the case pattern we extract each element of the (nested) tuples. In the expression above we sort by key, then by first element in value tuple and then second. On the other hand, this
data.toSeq.sortBy {case (k,(a,b)) => (k,b) }
will sort by key and last element in value tuple, and this
data.toSeq.sortBy {case (k,(a,b)) => (a,b) }
by the values of the map; this one
data.toSeq.sortBy {case (k,(a,b)) => (b) }
will sort by the last element in the values tuple.
Update As helpfully pointed out by #Paul a Map preserves no ordering, hence the result remains here as a sequence.
Is this what you're looking for?
data.toVector.sortBy(_._2)
This will sort the entries by the values (the tuples), where the order depends on both tuple arguments. The default sort behavior for a tuple is to sort on _1 and then _2:
Vector((2,1), (1,2), (1,3), (1,1)).sorted
// Vector((1,1), (1,2), (1,3), (2,1))
Sorting Tuples
First note that if you sort a collection of tuples you will get the same result as you expect, i.e. first items will be compared first and then second items.
For example (a1, b1) > (a2, b2) if and only if (a1 > a2) || ((a1 == a2) && (b1 > b2)).
So for getting the expected result in terms of sorting tuples you don't need to do anything.
Then it remains to how to sort a map based on values and how to preserve the order after sorting.
Sort map on values
You can use the sortBy method of List and then use an ordered data structure to preserve the ordering, like this:
new scala.collection.immutable.ListMap() ++ data.toList.sortBy(_._2)
If you run this in Scala REPL you will get the follwoing result:
scala> new scala.collection.immutable.ListMap() ++ data.toList.sortBy(_._2)
res3: scala.collection.immutable.ListMap[String,(Int, Long)] = Map(ip-11-232-132-31:9000 -> (0,1413669128111), ip-11-253-67-184:9000 -> (0,1413669134073), ip-11-232-142-77:9000 -> (0,1413669139043), ip-11-232-145-172:9000 -> (0,1413669141265), ip-11-254-25-225:9000 -> (1,1413669308124))
If you simply want to sort them and traverse the result (i.e. if you don't need a map as result) you don't even need to use ListMap.

Scala - finding a specific tuple in a list

Let's say we have this list of tuples:
val data = List(('a', List(1, 0)), ('b', List(1, 1)), ('c', List(0)))
The list has this signature:
List[(Char, List[Int])]
My task is to get the "List[Int]" element from a tuple inside "data" whose key is, for instance, letter "b". If I implement a method like "findIntList(data, 'b')", then I expect List(1, 1) as a result. I have tried the following approaches:
data.foreach { elem => if (elem._1 == char) return elem._2 }
data.find(x=> x._1 == ch)
for (elem <- data) yield elem match {case (x, y: List[Bit]) => if (x == char) y}
for (x <- data) yield if (x._1 == char) x._2
With all the approaches (except Approach 1, where I employ an explicit "return"), I get either a List[Option] or List[Any] and I don't know how to extract the "List[Int]" out of it.
One of many ways:
data.toMap.get('b').get
toMap converts a list of 2-tuples into a Map from the first element of the tuples to the second. get gives you the value for the given key and returns an Option, thus you need another get to actually get the list.
Or you can use:
data.find(_._1 == 'b').get._2
Note: Only use get on Option when you can guarantee that you'll have a Some and not a None. See http://www.scala-lang.org/api/current/index.html#scala.Option for how to use Option idiomatic.
Update: Explanation of the result types you see with your different approaches
Approach 2: find returns an Option[List[Int]] because it can not guarantee that a matching element gets found.
Approach 3: here you basically do a map, i.e. you apply a function to each element of your collection. For the element you are looking for the function returns your List[Int] for all other elements it contains the value () which is the Unit value, roughly equivalent to void in Java, but an actual type. Since the only common super type of ´List[Int]´ and ´Unit´ is ´Any´ you get a ´List[Any]´ as the result.
Approach 4 is basically the same as #3
Another way is
data.toMap.apply('b')
Or with one intermediate step this is even nicer:
val m = data.toMap
m('b')
where apply is used implicitly, i.e., the last line is equivalent to
m.apply('b')
There are multiple ways of doing it. One more way:
scala> def listInt(ls:List[(Char, List[Int])],ch:Char) = ls filter (a => a._1 == ch) match {
| case Nil => List[Int]()
| case x ::xs => x._2
| }
listInt: (ls: List[(Char, List[Int])], ch: Char)List[Int]
scala> listInt(data, 'b')
res66: List[Int] = List(1, 1)
You can try something like(when you are sure it exists) simply by adding type information.
val char = 'b'
data.collect{case (x,y:List[Int]) if x == char => y}.head
or use headOption if your not sure the character exists
data.collect{case (x,y:List[Int]) if x == char => y}.headOption
You can also solve this using pattern matching. Keep in mind you need to make it recursive though. The solution should look something like this;
def findTupleValue(tupleList: List[(Char, List[Int])], char: Char): List[Int] = tupleList match {
case (k, list) :: _ if char == k => list
case _ :: theRest => findTupleValue(theRest, char)
}
What this will do is walk your tuple list recursively. Check whether the head element matches your condition (the key you are looking for) and then returns it. Or continues with the remainder of the list.

arbitrary size tuple with first element type fixed

Say I have two lists
val L1 = List[(Int, Int)]((1,1), (2,2))
val L2 = List[(Int, Int, Int)]((1,1,1), (2,2,2))
Now I want to make a function func that takes in an Int value i and all items from both lists where the first element matches i. One way is
def func(i:Int) = {
L1.collect.collect{case any if any._1 != i => any}
L2.collect.collect{case any if any._1 != i => any}
}
considering that the two lines are so similar, it would be nice if code can be shortened. I am thinnking of some way where I could pass L1 (and L2) as a parameter to func. The function should not know in advance how many elements the tuple will have, just that the first element is Int.
Is this possible?
[EDIT: I think the question was not clear enough. My apologies.]
Here is what I want to do. I would like to do this on more than two lists, say n, by calling func several times, once for each list.
L1 = L1.collect.collect{case any if any._1 != i => any}
L2 = L2.collect.collect{case any if any._1 != i => any}
...
Ln = Ln.collect.collect{case any if any._1 != i => any}
where each L1, L2, ... Ln are lists of tuples with first element Int
[EDIT2]
In the above, L1 could be list of (Int, String), L2 could be of (Int, Int, Int), etc. The only guarantee is that the first element is Int.
def func(i:Int, lst: List[Product]) = {
lst.filter(_.productElement(0) == i)
}
Edited as per your edit & Dan's comment above.
Any Tuple (or, indeed, any case class) is a Product, so you can use the product iterator as a way to handle tuples of indeterminate size:
val x = (1,1,1,1)
x.productIterator forall {_ == 1} //returns true
as Product is a common supertype for the elements of both lists, you can simply concatenate them and filter:
val list1 = List((1,1), (2,2))
val list2 = List((1,1,1), (2,2,2))
(list1 ::: list2) filter {_.productIterator forall {_ == 1}}
//returns List[Product with Serializable] = List((1,1), (1,1,1))
UPDATE
To filter just a single list on the first element of contained products:
val list1 = List((1,1), (2,2))
list1 filter {_.productIterator.next() == 1}