arbitrary size tuple with first element type fixed - scala

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}

Related

How do you write a function to divide the input list into three sublists?

Write a function to divide the input list into three sublists.
The first sub-list is to include all the elements whose indexes satisfy the equation i mod 3 = 1.
The second sub-list is to include all the elements whose indexes satisfy the equation and mod 3 = 2.
The third sub-list is to contain the remaining elements.
The order of the elements must be maintained. Return the result as three lists.
Write a function using tail and non-tail recursion.
My attempt: I’m very confused in how to increase index so it can go through the list, any recommendation about how to make it recursive with increasing index each time?
def divide(list: List[Int]): (List[Int], List[Int], List[Int]) = {
var index:Int =0
def splitList(remaining: List[Int], firstSubList: List[Int], secondSubList: List[Int], thirdSubList: List[Int], index:Int): (List[Int], List[Int], List[Int]) = {
if(remaining.isEmpty) {
return (List[Int](), List[Int](), List[Int]())
}
val splitted = splitList(remaining.tail, firstSubList, secondSubList, thirdSubList, index)
val firstList = if (index % 3 == 1) List() ::: splitted._1 else splitted._1
val secondList = if (index % 3 == 2) List() ::: splitted._2 else splitted._2
val thirdList = if((index% 3 != 1) && (index % 3 != 2)) List() ::: splitted._3 else splitted._3
index +1
(firstSubList ::: firstList, secondSubList ::: secondList, thirdSubList ::: thirdList)
}
splitList(list, List(), List(), List(), index+1)
}
println(divide(List(0,11,22,33)))
Generalizing the requirement a little, here's one approach using a simple recursive function to compose a Map of Lists by modulo n of the original list indexes:
def splitList[T](list: List[T], n: Int): Map[Int, List[T]] = {
#scala.annotation.tailrec
def loop(zls: List[(T, Int)], lsMap: Map[Int, List[T]]): Map[Int, List[T]] =
zls match {
case Nil =>
lsMap.map{ case (i, ls) => (i, ls.reverse) }
case (x, i) :: rest =>
val j = i % n
loop(rest, lsMap + (j -> (x :: lsMap.getOrElse(j, Nil))))
}
loop(list.zipWithIndex, Map.empty[Int, List[T]])
}
splitList(List(0, 11, 22, 33, 44, 55, 66), 3)
// Map(0 -> List(0, 33, 66), 1 -> List(11, 44), 2 -> List(22, 55))
splitList(List('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i'), 4)
// Map(0 -> List(a, e, i), 1 -> List(b, f), 2 -> List(c, g), 3 -> List(d, h))
To do this in real life, just label each value with its index and then group by that index modulo 3:
def divide[T](list: List[T]) = {
val g = list.zipWithIndex.groupMap(_._2 % 3)(_._1)
(g.getOrElse(1, Nil), g.getOrElse(2, Nil), g.getOrElse(0, Nil))
}
If you insist on a recursive version, it might look like this:
def divide[T](list: List[T]) = {
def loop(rem: List[T]): (List[T], List[T], List[T]) =
rem match {
case a::b::c::tail =>
val rem = loop(tail)
(b +: rem._1, c +: rem._2, a +: rem._3)
case a::b::Nil =>
(List(b), Nil, List(a))
case a::Nil =>
(Nil, Nil, List(a))
case Nil =>
(Nil, Nil, Nil)
}
loop(list)
}
Tail recursion would look like this:
def divide[T](list: List[T]) = {
#annotation.tailrec
def loop(rem: List[T], res: (List[T], List[T], List[T])): (List[T], List[T], List[T]) =
rem match {
case a::b::c::tail =>
loop(tail, (res._1 :+ b, res._2 :+ c, res._3 :+ a))
case a::b::Nil =>
(res._1 :+ b, res._2, res._3 :+ a)
case a::Nil =>
(res._1, res._2, res._3 :+ a)
case Nil =>
res
}
loop(list, (Nil, Nil, Nil))
}
And if you care about efficiency, this version would build the lists in the other order and reverse them when returning the result.
Your problem is that you put index+1 into a wrong place. Try swapping it around: put index+1 into the call where you have index, and index into the other one. Also remove the "standalone" index+1 statement in the middle, it doesn't do anything anyway.
That should make your code work ... but it is still not very good. A couple of problems with it (besides it being badly structured, non-idiomatic, and hard to read, which is kinda subjective):
It it is not tail-recursive, and effectively, creates another copy of the entire list on stack. This may be problematic when the list is long.
It concatenates (potentially long) lists. This is a bad idea. List in scala is a singly linked list, you have it's head readily available, but to get to the end, you need to spend O(N) cycles, iterating through each node. Thus things like foo:::bar in a iterative function instantly make any algorithm (at least) quadratic.
The usual "trick" to avoid the last problem is prepending elements to output one-by-one, and then reversing the result in the end. The first one can be avoided with tail-recursion. The "non-idiomatic" and "hard to read" problems are mostly addressed by using match statement in this case:
def split3(
in: List[Int],
one: List[Int],
two: List[Int],
three: List[Int],
index: Int = 0
} = (in, index % 3) match {
case (Nil, _) => (one.reverse, two.reverse, three.reverse)
case (head::tail, 1) => split3(tail, head::one, two, three, index+1)
case (head::tail, 2) => split3(tail, one, head::two, three, index+1)
case (head::tail, _) => split3(tail, one, two, head::three, index+1)
}
Now, this is a fine solution, albeit a little repetitive to my demanding eye ... But if want to be clever and really unleash the full power of scala standard library, forget recursion, you don't really need it in this case.
If you knew that number of elements in the list was always divisible by 3,
you could just do list.grouped(3).toSeq.transpose: break the list into groups of three (each group will have index%3=0 as first element, index%3=1 as second, index%3=2 as the third), and then transpose will turn a list of lists of 3 into a list of three lists where the first one contains all the first elements, the second - all the seconds etc. (I know, you wanted them in a different order, but that's trivial). If you are having trouble understanding what I am talking about, just try running it on some lists, and look at the results.
This would be a really elegant solution ... if it worked :/ The problem is, that it only does when you have 3*n elements in the original list. If not, transpose will fail on the last element if it doesn't have 3 elements like all others. Can we fix it? Well ... that's where the cleverness comes in.
val (triplets, tails) = list.grouped(3).toSeq.partition(_.size == 3)
triplets
.transpose
.padTo(3, Nil)
.zip(tails.flatten.map(Seq(_)).padTo(3, Nil))
.map { case (head, tail) => head ++ tail }
Basically, it is doing the same thing as the one-liner I described above (break into groups of 3 and transpose), but adds special handling for the case when the last group has less than three elements - it splits it out and pads with required number of empty lists, then just appends the result to transposed triplets.

Scala - Using Filter to see if the value in one list matches the value in the same index in another list

Given 2 Lists and using the Filter method, i am required to write a function that will take these 2 lists, filter through them and then compare if the value in one index on one list matches the value in the same index on the other list
Example VVV
scala> val list1 = List(1,2,3,10)
scala> val list2 = List(3,2,1,10)
scala> val mn = matchedNumbers(list1, list2)
List(2,10)
The method is called "matchedNumbers"
Any help would be appreciated. Thanks
The solution is almost the same as of #talex, only using collect:
def matchedNumbers(list1: List[Int], list2: List[Int]) =
list1.zip(list2).collect{case (x, y) if x == y => x}
You can use this
def matchedNumbers(list1: List[Int], list2: List[Int]) = {
list1.zip(list2).filter { case (x, y) => x == y }.map(_._1)
}

GroupBy using two conditions in 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.

Finding character in 2 dimensional scala list

So this might not be the best way to tackle it but my initial thought was a for expression.
Say I have a List like
List(List('a','b','c'),List('d','e','f'),List('h','i','j'))
I would like to find the row and column for a character, say 'e'.
def findChar(letter: Char, list: List[List[Char]]): (Int, Int) =
for {
r <- (0 until list.length)
c <- (0 until list(r).length)
if list(r)(c) == letter
} yield (r, c)
If there is a more elegant way I'm all ears but I would also like to understand what's wrong with this. Specifically the error the compiler gives me here is
type mismatch; found : scala.collection.immutable.IndexedSeq[(Int, Int)] required: (Int, Int)
on the line assigning to r. It seems to be complaining that my iterator doesn't match the return type but I don't quite understand why this is or what to do about it ...
In the signature of findChar you are telling the compiler that it returns (Int, Int). However, the result of your for expression (as inferred by Scala) is IndexedSeq[(Int, Int)] as the error message indicates. The reason is that (r, c) after yield is produced for every "iteration" in the for expression (i.e., you are generating a sequence of results, not just a single result).
EDIT: As for findChar, you could do:
def findChar(letter: Char, list: List[List[Char]]) = {
val r = list.indexWhere(_ contains letter)
val c = list(r).indexOf(letter)
(r, c)
}
It is not the most efficient solution, but relatively short.
EDIT: Or reuse your original idea:
def findAll(letter: Char, list: List[List[Char]]) =
for {
r <- 0 until list.length
c <- 0 until list(r).length
if list(r)(c) == letter
} yield (r, c)
def findChar(c: Char, xs: List[List[Char]]) = findAll(c, xs).head
In both cases, be aware that an exception occurs if the searched letter is not contained in the input list.
EDIT: Or you write a recursive function yourself, like:
def findPos[A](c: A, list: List[List[A]]) = {
def aux(i: Int, xss: List[List[A]]) : Option[(Int, Int)] = xss match {
case Nil => None
case xs :: xss =>
val j = xs indexOf c
if (j < 0) aux(i + 1, xss)
else Some((i, j))
}
aux(0, list)
}
where aux is a (locally defined) auxiliary function that does the actual recursion (and remembers in which sublist we are, the index i). In this implementation a result of None indicates that the searched element was not there, whereas a successful result might return something like Some((1, 1)).
For your other ear, the question duplicates
How to capture inner matched value in indexWhere vector expression?
scala> List(List('a','b','c'),List('d','e','f'),List('h','i','j'))
res0: List[List[Char]] = List(List(a, b, c), List(d, e, f), List(h, i, j))
scala> .map(_ indexOf 'e').zipWithIndex.find(_._1 > -1)
res1: Option[(Int, Int)] = Some((1,1))

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.