How to create an immutable map/set from a seq? - scala

I am try to construct immutable Sets/Maps from a Seq. I am currently doing the following:
val input: Seq[(String, Object)] = //.....
Map[String, Object]() ++ input
and for sets
val input: Seq[String] = //.....
Set[String]() ++ input
Which seems a little convoluted, is there a better way?

In Scala 2.8:
Welcome to Scala version 2.8.0.r20327-b20091230020149 (Java HotSpot(TM) Client VM, Java 1.6.
Type in expressions to have them evaluated.
Type :help for more information.
scala> val seq: Seq[(String,Object)] = ("a","A")::("b","B")::Nil
seq: Seq[(String, java.lang.Object)] = List((a,A), (b,B))
scala> val map = Map(seq: _*)
map: scala.collection.immutable.Map[String,java.lang.Object] = Map(a -> A, b -> B)
scala> val set = Set(seq: _*)
set: scala.collection.immutable.Set[(String, java.lang.Object)] = Set((a,A), (b,B))
scala>
Edit 2010.1.12
I find that there is a more simple way to create set.
scala> val seq: Seq[(String,Object)] = ("a","A")::("b","B")::Nil
seq: Seq[(String, java.lang.Object)] = List((a,A), (b,B))
scala> val set = seq.toSet
set: scala.collection.immutable.Set[(String, java.lang.Object)] = Set((a,A), (b,B))

To convert a Seq to a Map, simply call toMap on the Seq. Note that the elements of the Seq must be Tuple2 ie. (X,Y) or (X->Y)
scala> val seq: Seq[(String,String)] = ("A","a")::("B","b")::("C","c")::Nil
seq: Seq[(java.lang.String, java.lang.String)] = List((A,a), (B,b), (C,c))
scala> seq.toMap
res0: scala.collection.immutable.Map[java.lang.String,java.lang.String] = Map((A,a), (B,b), (C,c))
To convert a Seq to a Set, simply call toSet on the Seq.
scala> val seq: Seq[String] = "a"::"b"::"c"::Nil
seq: Seq[java.lang.String] = List(a, b, c)
scala> seq.toSet
res1: scala.collection.immutable.Set[java.lang.String] = Set(a, b, c)

Related

scala: how to rectify "option" type after leftOuterJoin

Given
scala> val rdd1 = sc.parallelize(Seq(("a",1),("a",2),("b",3)))
scala> val rdd2 = sc.parallelize(Seq("a",5),("c",6))
scala> val rdd3 = rdd1.leftOuterJoin(rdd2)
scala> rdd3.collect()
res: Array[(String, (Int, Option[Int]))] = Array((a,(1,Some(5))), (a,(2,Some(5))), (b,(3,None)))
We can see that the data type of "Option[Int]" in rdd3. Is there a way to rectify this so that rdd3 can be Array[String, (Int, Int)]? Suppose we can specify a value (e.g. 999) for the "None".
scala> val result = rdd3.collect()
scala> result.map(t => (t._1, (t._2._1, t._2._2.getOrElse(999))))
This should do it.

how to iterate over list of lists in scala

I want to iterate over a list[List[String]] to subset it into a list[String] and then store each list from the list of lists in a val. The val's name could be anything but it should include each list index in its name.
For example:
val x: List[ List[String]] = List(List("Nike","Apple"), List("James", "Mike"))
Desired output:
group_0 : List[String] = List(Nike, Apple)
group_1 = List[String] = List(James, Mike)
Use zipWithIndex and convert into Map[String, List[String]]. Each key will be of form group_0, group_1 etc
val map = x.zipWithIndex.map(x => s"group_${x._2}" -> x._1).toMap
Access each list using key
map("group_0")
Scala REPL
scala> x.zipWithIndex.map(x => s"group_${x._2}" -> x._1).toMap
res4: scala.collection.immutable.Map[String,List[String]] = Map(group_0 -> List(Nike, Apple), group_1 -> List(James, Mike))
scala> res4("group_0")
res6: List[String] = List(Nike, Apple)
scala> res4("group_1")
res7: List[String] = List(James, Mike)
#Manoj Kumar Dhakd idea to use toMap is better
use function zipWithIndex and zip index with your lists then use map and create a Map
val listMap=x.zipWithIndex.map(grp=>"group_"+grp._2.toString->grp._1).toMap
Display your output like below
listMap.foreach(x=>println(x._1+"="+x._2))
//output:
group_0=List(Nike, Apple)
group_1=List(James, Mike)
x.zipWithIndex.foreach{case (x,y)=>println("group_"+y+": List[String] = "+x)}
Then, in Scala REPL:
scala> val x: List[ List[String]] = List(List("Nike","Apple"), List("James", "Mike"))
x: List[List[String]] = List(List(Nike, Apple), List(James, Mike))
scala> x.zipWithIndex.foreach{case (x,y)=>println("group_"+y+": List[String] = "+x)}
group_0: List[String] = List(Nike, Apple)
group_1: List[String] = List(James, Mike)

Scala: Append only Some's to immutable list

So say we're given a List[String] and bunch of Option[String]'s call them a, b, c. Say I want to append the valid (Some's) Options[String]'s out of a, b, c to my existingList[String]. What would be the best way to go about this using immutable structures?
I.e. I know I could use a ListBuffer and do something like:
def foo(a: Option[String], b: Option[String], c: Option[String]) : ListBuffer[String] = {
val existingList = new ListBuffer("hey")
a.map(_ => existingList += _)
b.map(_ => existingList += _)
c.map(_ => existingList += _)
}
but I want to use immutable structures.
Use .flatten on a list of options and append it to your list
val existingList = List(1, 2, 3)
val a = Some(4)
val b = None
val c = Some(5)
val newList = existingList ::: List(a, b, c).flatten
def foo(a: Option[String], b: Option[String], c: Option[String]): List[String] =
List("hey") ++ a.toList ++ b.toList ++ c.toList
which is similar to flatten or flatMap.
scala> foo(Some("a"), None, Some("c"))
res1: List[String] = List(hey, a, c)
It's better to define a generic function like this:
def foo[T](xs: Option[T]*) : List[T] =
xs.toList.flatten
scala> foo(Some("a"), None, Some("c"))
res2: List[String] = List(a, c)
Let val list = List("A", "B", "C") and val opts = = List(Some("X"), None, Some("Y"), None, Some("Z")). Then list ++ opts.filter(_.isDefined).map(_.get) will give an new List("A", "B", "C", "X", "Y", "Z") with all elements from list and all non-empty elements of opts.

Use 4 (or N) collections to yield only one value at a time (1xN) (i.e. zipped for tuple4+)

scala> val a = List(1,2)
a: List[Int] = List(1, 2)
scala> val b = List(3,4)
b: List[Int] = List(3, 4)
scala> val c = List(5,6)
c: List[Int] = List(5, 6)
scala> val d = List(7,8)
d: List[Int] = List(7, 8)
scala> (a,b,c).zipped.toList
res6: List[(Int, Int, Int)] = List((1,3,5), (2,4,6))
Now:
scala> (a,b,c,d).zipped.toList
<console>:12: error: value zipped is not a member of (List[Int], List[Int], List[Int], List[Int])
(a,b,c,d).zipped.toList
^
I've searched for this elsewhere, including this one and this one, but no conclusive answer.
I want to do the following or similar:
for((itemA,itemB,itemC,itemD) <- (something)) yield itemA + itemB + itemC + itemD
Any suggestions?
Short answer:
for (List(w,x,y,z) <- List(a,b,c,d).transpose) yield (w,x,y,z)
// List[(Int, Int, Int, Int)] = List((1,3,5,7), (2,4,6,8))
Why you want them as tuples, I'm not sure, but a slightly more interesting case would be when your lists are of different types, and for example, you want to combine them into a list of objects:
case class Person(name: String, age: Int, height: Double, weight: Double)
val names = List("Alf", "Betty")
val ages = List(22, 33)
val heights = List(111.1, 122.2)
val weights = List(70.1, 80.2)
val persons: List[Person] = ???
Solution 1: using transpose, as above:
for { List(name: String, age: Int, height: Double, weight: Double) <-
List(names, ages, heights, weights).transpose
} yield Person(name, age, height, weight)
Here, we need the type annotations in the List extractor, because transpose gives a List[List[Any]].
Solution 2: using iterators:
val namesIt = names.iterator
val agesIt = ages.iterator
val heightsIt = heights.iterator
val weightsIt = weights.iterator
for { name <- names }
yield Person(namesIt.next, agesIt.next, heightsIt.next, weightsIt.next)
Some people would avoid iterators because they involve mutable state and so are not "functional". But they're easy to understand if you come from the Java world and might be suitable if what you actually have are already iterators (input streams etc).
Shameless plug-- product-collections does something similar:
a flatZip b flatZip c flatZip d
res0: org.catch22.collections.immutable.CollSeq4[Int,Int,Int,Int] =
CollSeq((1,3,5,7),
(2,4,6,8))
scala> res0(0) //first row
res1: Product4[Int,Int,Int,Int] = (1,3,5,7)
scala> res0._1 //first column
res2: Seq[Int] = List(1, 2)
val g = List(a,b,c,d)
val result = ( g.map(x=>x(0)), g.map(x=>x(1) ) )
result : (List(1, 3, 5, 7),List(2, 4, 6, 8))
basic, zipped assit tuple2 , tuple3
http://www.scala-lang.org/api/current/index.html#scala.runtime.Tuple3Zipped
so, You want 'tuple4zippped' you make it
gool luck
found a possible solution, although it's very imperative to my taste:
val a = List(1,2)
val b = List(3,4)
val c = List(5,6)
val d = List(7,8)
val g : List[Tuple4[Int,Int,Int,Int]] = {
a.zipWithIndex.map { case (value,index) => (value, b(index), c(index), d(index))}
}
zipWithIndex would allow me to go through all the other collections. However, i'm sure there's a better way to do this. Any suggestions?
Previous attempts included:
Ryan LeCompte's zipMany or transpose.
however, it a List, not a tuple4. this is not as convenient to work with since i can't name the variables.
Tranpose it's already built in in the standard library and doesn't require higher kinds imports so it's preferrable, but not ideal.
I also, incorrectly, tried the following example with Shapeless
scala> import Traversables._
import Tuples._
import Traversables._
import Tuples._
import scala.language.postfixOps
scala> val a = List(1,2)
a: List[Int] = List(1, 2)
scala> val b = List(3,4)
b: List[Int] = List(3, 4)
scala> val c = List(5,6)
c: List[Int] = List(5, 6)
scala> val d = List(7,8)
d: List[Int] = List(7, 8)
scala> val x = List(a,b,c,d).toHList[Int :: Int :: Int :: Int :: HNil] map tupled
x: Option[(Int, Int, Int, Int)] = None

How would I yield an immutable.Map in Scala?

I have tried this but it does not work:
val map:Map[String,String] = for {
tuple2 <- someList
} yield tuple2._1 -> tuple2._2
How else would I convert a List of Tuple2s into a Map?
It couldn't be simpler:
Map(listOf2Tuples: _*)
using the apply method in Map companion object.
My First try is this:
scala> val country2capitalList = List("England" -> "London", "Germany" -> "Berlin")
country2capitalList: List[(java.lang.String, java.lang.String)] = List((England,London), (Germany,Berlin))
scala> val country2capitalMap = country2capital.groupBy(e => e._1).map(e => (e._1, e._2(0)._2))
country2capitalMap: scala.collection.Map[java.lang.String,java.lang.String] = Map(England -> London, Germany -> Berlin)
But here is the best solution:
scala> val betterConversion = Map(country2capitalList:_*)
betterConversion: scala.collection.immutable.Map[java.lang.String,java.lang.String] = Map(England -> London, Germany -> Berlin)
The :_* is needed to give the compiler a hint to use the list as a varargs argument. Otherwise it will give you:
scala> Map(country2capitalList)
<console>:6: error: type mismatch;
found : List[(java.lang.String, java.lang.String)]
required: (?, ?)
Map(country2capitalList)
^
From Scala 2.8 on you can use toMap:
scala> val someList = List((1, "one"), (2, "two"))
someList: List[(Int, java.lang.String)] = List((1,one), (2,two))
scala> someList.toMap
res0: scala.collection.immutable.Map[Int,java.lang.String] = Map((1,one), (2,two))
In 2.8, you can use the toMap method:
scala> val someList = List((1, "one"), (2, "two"))
someList: List[(Int, java.lang.String)] = List((1,one), (2,two))
scala> someList.toMap
res0: scala.collection.immutable.Map[Int,java.lang.String] = Map((1,one), (2,two))
This will work for any collection of pairs. Note that the documentation has this to say about its duplicate policy:
Duplicate keys will be overwritten by
later keys: if this is an unordered
collection, which key is in the
resulting map is undefined.
In scala 2.8:
scala> import scala.collection.breakOut
import scala.collection.breakOut
scala> val ls = List("a","bb","ccc")
ls: List[java.lang.String] = List(a, bb, ccc)
scala> val map: Map[String,Int] = ls.map{ s => (s,s.length) }(breakOut)
map: Map[String,Int] = Map((a,1), (bb,2), (ccc,3))
scala> val map2: Map[String,Int] = ls.map{ s => (s,s.length) }.toMap
map2: Map[String,Int] = Map((a,1), (bb,2), (ccc,3))
scala>