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.
Related
I have this prbolem, I have an RDD[(String,String, List[String]), and I would like to "flatmap" it to obtain a RDD[(String,String, String)]:
e.g:
val x :RDD[(String,String, List[String]) =
RDD[(a,b, list[ "ra", "re", "ri"])]
I would like get:
val result: RDD[(String,String,String)] =
RDD[(a, b, ra),(a, b, re),(a, b, ri)])]
Use flatMap:
val rdd = sc.parallelize(Seq(("a", "b", List("ra", "re", "ri"))))
// rdd: org.apache.spark.rdd.RDD[(String, String, List[String])] = ParallelCollectionRDD[7] at parallelize at <console>:28
rdd.flatMap{ case (x, y, z) => z.map((x, y, _)) }.collect
// res23: Array[(String, String, String)] = Array((a,b,ra), (a,b,re), (a,b,ri))
This is an alternative way of doing it using flatMap again
val rdd = sparkContext.parallelize(Seq(("a", "b", List("ra", "re", "ri"))))
rdd.flatMap(array => array._3.map(list => (array._1, array._2, list))).foreach(println)
I have a scala list of following structure:
val list = List(
List(a1,a2,a3,a4,a5),
List(b1,b2,b3,b4,b5),
List(c1,c2,c3,c4,c5)
)
From this list, I want to generate a list which would look like:
List(
List(a1,b1,c1),
List(a2,b2,c2),
List(a3,b3,c3),
List(a4,b4,c4),
List(a5,b5,c5)
)
How do I achieve this in scala ?
If you have a fixed number of such lists, you can use zipped to zip them together (into tuples). As in your example:
scala> (List(a1,a2,a3,a4), List(b1,b2,b3,b4), List(c1,c2,c3,c4)).zipped.toList
List((a1,b1,c1),(a2,b2,c2),(a3,b3,c3),(a4,b4,c4))
If you really want the full power of what you have above, it is just the transpose of your list of lists:
scala> List(List(a1,a2,a3,a4), List(b1,b2,b3,b4), List(c1,c2,c3,c4)).transpose
List(List(a1,b1,c1),List(a2,b2,c2),List(a3,b3,c3),List(a4,b4,c4))
Here is a manual implementation of the library function.
object CustomZipTest extends App{
val list1: List[String] = List("a", "1", "w")
val list2: List[String] = List("b", "2", "x")
val list3: List[String] = List("c", "3", "y")
val list4: List[String] = List("d", "4", "z")
def customZip[T](list: List[T] *): List[List[T]] = {
def stripOneLevel(list: Seq[List[T]]): (Seq[List[T]], List[T]) = {
list.foldRight(Seq(): Seq[List[T]], List(): List[T]){ (e, acc) =>
if (e.size == 1) (acc._1, e.head +: acc._2)
else (e.tail +: acc._1, e.head +: acc._2)
}
}
def strip(list: Seq[List[T]], acc: List[List[T]]): List[List[T]] = {
list match {
case Nil => acc
case x+:xs =>
val stripped = stripOneLevel(list)
strip(stripped._1, stripped._2 +: acc)
}
}
strip(list, List()).reverse
}
println(customZip(list1, list2, list3, list4))
}
I often see these two patterns for appending an Optional value to a Seq:
def example1(fooList: Seq[Foo], maybeFoo: Option[Foo]): Seq[Foo]) = {
if (maybeFoo.isDefined)
fooList :+ maybeFoo.get
else
fooList
}
def example2(fooList: Seq[Foo], maybeFoo: Option[Foo]): Seq[Foo]) = {
maybeFoo match {
case Some(foo) => fooList :+ foo
case None => fooList
}
}
Both of those methods work, but they seem verbose and ugly. Is there an existing operator or method to do this more naturally/functionally?
Thanks!
Option implicitly converts into a sequence with 1 or 0 items so the following works:
scala> val opt = Some("a")
opt: Some[String] = Some(a)
scala> val nope = None
nope: None.type = None
scala> val seq = Seq("a", "b", "c")
seq: Seq[String] = List(a, b, c)
scala> seq ++ opt
res3: Seq[String] = List(a, b, c, a)
scala> seq ++ nope
res4: Seq[String] = List(a, b, c)
maybeFoo.foldLeft(fooList)(_ :+ _)
Assume we have the following lists of different size:
val list1 = ("a", "b", "c")
val list2 = ("x", "y")
Now I want to merge these 2 lists and create a new list with the string elements being concatenated:
val desiredResult = ("ax", "by", "c")
I tried
val wrongResult = (list1, list2).zipped map (_ + _)
as proposed here, but this doesn't work as intended, because zip discards those elements of the longer list that can't be matched.
How can I solve this problem? Is there a way to zip the lists and give a "default element" (like the empty string in this case) if one list is longer?
The method you are looking for is .zipAll:
scala> val list1 = List("a", "b", "c")
list1: List[String] = List(a, b, c)
scala> val list2 = List("x", "y")
list2: List[String] = List(x, y)
scala> list1.zipAll(list2, "", "")
res0: List[(String, String)] = List((a,x), (b,y), (c,""))
.zipAll takes 3 arguments:
the iterable to zip with
the default value if this (the collection .zipAll is called on) is shorter
the default value if the other collection is shorter
The API-based zipAll is the way to go, yet you can implement it (as an exercise) for instance as follows,
implicit class OpsSeq[A,B](val xs: Seq[A]) extends AnyVal {
def zipAll2(ys: Seq[B], xDefault: A, yDefault: B) = {
val xs2 = xs ++ Seq.fill(ys.size-xs.size)(xDefault)
val ys2 = ys ++ Seq.fill(xs.size-ys.size)(yDefault)
xs2.zip(ys2)
}
}
Hence for instance
Seq(1,2).zipAll2(Seq(3,4,5),10,20)
List((1,3), (2,4), (10,5))
and
list1.zipAll2(list2, "", "")
List((a,x), (b,y), (c,""))
A recursive version,
def zipAll3[A,B](xs: Seq[A], ys: Seq[B], xd: A, yd: B): Seq[(A,B)] = {
(xs,ys) match {
case (Seq(), Seq()) => Seq()
case (x +: xss, Seq()) => (x,yd) +: zipAll3(xss, Seq(), xd, yd)
case (Seq(), y +: yss) => (xd,y) +: zipAll3(Seq(), yss, xd, yd)
case (x +: xss, y +: yss) => (x,y) +: zipAll3(xss, yss, xd, yd)
}
}
with default xd and default yd values.
This is a follow-up to one of my recent previous questions:
I would like to define a zip Applicative instance for List (and probably Set and Map). For example:
val xs: List[Int] = List(1, 2, 3)
val fs: List[Int => Int] = List(f1, f2, f3)
val ys: List[Int] = xs <*> fs // expected to be List(f1(1), f2(2), f3(3))
So I defined a ZipList and its Applicative:
case class ZipList[A](val list: List[A])
implicit val zipListApplicative = new Applicative[ZipList] {
def point[A](a: => A): ZipList[A] = ZipList(List(a))
def ap[A, B](za: => ZipList[A])(zf: => ZipList[A => B]): ZipList[B] = {
val bs = (za.list zip zf.list) map {case (a, f) => f(a)}
ZipList(bs)
}
}
and can use it as follows:
scala> val xs: List[Int] = List(1, 2, 3)
xs: List[Int] = List(1, 2, 3)
scala> val fs: List[Int => Int] = List(_ + 2, _ + 2, _ +1)
fs: List[Int => Int] = List(<function1>, <function1>, <function1>)
scala> ZipList(xs) <*> ZipList(fs)
res4: ZipList[Int] = ZipList(List(3, 4, 4))
It seems to be working but maybe I am missing something.
Does zipListApplicative comply to the applicative laws ?
Is ZipList supposed to be a stream because the point should generate an infinite stream of values ? Why ?
Applicatives should satisfy the law
point identity <*> v == v
which yours does not since
point identity List(1,2,3) == List(1)
pure a for a zip list should return an infinite stream of a which is why you need a lazy data structure.