Loop and create list in Scala - scala

I am getting empty list when I am trying to create the list with :: operator. My code looks like this:
def getAllInfo(locks: List[String]): List[LockBundle] = DB.withTransaction { implicit s =>
val myList = List[LockBundle]()
locks.foreach(
l => findForLock(l) :: myList
)
myList
}
def findForLock(lock: String): Option[LockBundle] = { ... }
Any suggestion?

Use flatMap
locks.flatMap(l => findForLock(l))
Your code becomes
def getAllInfo(locks: List[String]): List[LockBundle] = DB.withTransaction { implicit s =>
locks.flatMap(l => findForLock(l))
}
Alternatively you could use map and flatten. Something like this locks.map(l => findForLock(l)).flatten
Functional programming is all about transformations. You just have to transform your existing list into another list using a transformation which is your function findForLock.
Problem with your code
val myList = List[LockBundle]()
locks.foreach(
l => findForLock(l) :: myList
)
myList
First of all foreach returns Unit so, you use foreach for side effecting operations and not transformations. As you need transformation so do not use foreach.
Next, findForLock(l) :: myList gives you a value but this gets ignored as there is no one who is storing the value generated. So, in order to store the value use accumulator and pass it as a function parameter in case of recursion.
Correcting your code
If you want to do in your way. You need to use the accumulator.
First fix your types findForLock(l) returns Option, You list is of type List[LockBundle] so change the list type to List[Option[LockBundle]].
In order to get List[LockBundle] from List[Option[LockBundle]] Just do flatten on List[Option[LockBundle]] list. See below code snippet
var myList = List[Option[LockBundle]]()
locks.foreach(
l => myList = findForLock(l) :: myList
)
myList.flatten
The above way is not functional and is not recommended.

Your code doesn't work, because foreach combinator calls given closure for each element, but all you do here is to return expression findForLock(l) :: myList which is discarded.
As pamu suggested, you can use flatMap on a function to map each element to values returned by findForLock and flatten that list, which turns Option into element of the list if it's Some or nothing if it's None.
Keep in mind that this works only because there is an implicit conversion from Option to Seq, in general flatMap works only if you return the same type as given monad (that in this case is List or Option).

Related

Scala: Find and update one element in a list

I am trying to find an elegant way to do:
val l = List(1,2,3)
val (item, idx) = l.zipWithIndex.find(predicate)
val updatedItem = updating(item)
l.update(idx, updatedItem)
Can I do all in one operation ? Find the item, if it exist replace with updated value and keep it in place.
I could do:
l.map{ i =>
if (predicate(i)) {
updating(i)
} else {
i
}
}
but that's pretty ugly.
The other complexity is the fact that I want to update only the first element which match predicate .
Edit: Attempt:
implicit class UpdateList[A](l: List[A]) {
def filterMap(p: A => Boolean)(update: A => A): List[A] = {
l.map(a => if (p(a)) update(a) else a)
}
def updateFirst(p: A => Boolean)(update: A => A): List[A] = {
val found = l.zipWithIndex.find { case (item, _) => p(item) }
found match {
case Some((item, idx)) => l.updated(idx, update(item))
case None => l
}
}
}
I don't know any way to make this in one pass of the collection without using a mutable variable. With two passes you can do it using foldLeft as in:
def updateFirst[A](list:List[A])(predicate:A => Boolean, newValue:A):List[A] = {
list.foldLeft((List.empty[A], predicate))((acc, it) => {acc match {
case (nl,pr) => if (pr(it)) (newValue::nl, _ => false) else (it::nl, pr)
}})._1.reverse
}
The idea is that foldLeft allows passing additional data through iteration. In this particular implementation I change the predicate to the fixed one that always returns false. Unfortunately you can't build a List from the head in an efficient way so this requires another pass for reverse.
I believe it is obvious how to do it using a combination of map and var
Note: performance of the List.map is the same as of a single pass over the list only because internally the standard library is mutable. Particularly the cons class :: is declared as
final case class ::[B](override val head: B, private[scala] var tl: List[B]) extends List[B] {
so tl is actually a var and this is exploited by the map implementation to build a list from the head in an efficient way. The field is private[scala] so you can't use the same trick from outside of the standard library. Unfortunately I don't see any other API call that allows to use this feature to reduce the complexity of your problem to a single pass.
You can avoid .zipWithIndex() by using .indexWhere().
To improve complexity, use Vector so that l(idx) becomes effectively constant time.
val l = Vector(1,2,3)
val idx = l.indexWhere(predicate)
val updatedItem = updating(l(idx))
l.updated(idx, updatedItem)
Reason for using scala.collection.immutable.Vector rather than List:
Scala's List is a linked list, which means data are access in O(n) time. Scala's Vector is indexed, meaning data can be read from any point in effectively constant time.
You may also consider mutable collections if you're modifying just one element in a very large collection.
https://docs.scala-lang.org/overviews/collections/performance-characteristics.html

Removing Try failures in collection using flatMap

I have a Map[String, String]
How can I simply this expression using flatMap?
val carNumbers = carMap.keys.map(k => Try(k.stripPrefix("car_number_").toInt)).toList.filter(_.isSuccess)
Note: I want to remove the Failure/Success wrapper and just have a List[Int].
It looks like you just want to convert Try to Option:
for {
key <- carMap.keys
t <- Try(key.stripPrefix("car_number_").toInt).toOption
} yield t
this will result Iterable and you can convert it to list with .toList method.
Also you can go with oneliner like this:
carMap.keys.flatMap(k => Try(k.stripPrefix("car_number_").toInt).toOption)
Consider using collect() with a partial function:
carMap.keys
.collect( k =>
Try(k.stripPrefix("car_number_").toInt) match {
case Success(num) => num
}
)
This will return an Iterable[Int] with the values that could be stripped and converted to an Int (assuming this is what you were looking for).

Apply a sequence of functions to value and get the final result

I wish to apply a sequence of functions to an object (each of the functions may return the same or modified object) and get the ultimate result returned by the last function.
Is there an idiomatic Scala way to turn this (pseudocode):
val pipeline = ListMap(("a" -> obj1), ("b" -> obj2), ("c" -> obj3))
into this?
val initial_value = Something("foo", "bar")
val result = obj3.func(obj2.func(obj1.func(initial_value)))
The pipeline is initialized at runtime and contains an undetermined number of "manglers".
I tried with foreach but it requires an intermediate var to store the result, and foldLeft only works on types of ListMap, while the initial value and the result are of type Something.
Thanks
This should do it:
pipeline.foldLeft(initial_value){case (acc, (k,obj)) => obj.func(acc)}
No idea why pipeline contains pairs, though.
Assuming input and output types are the same, I'd go with a reduceLeft and composition by andThen:
def pipe[A](a: A, funcs: List[A => A]): A = funcs.reduceLeft(_ andThen _)(a)
I think foldLeft is the right choice:
val pipeline = List("a"-> func1, "b"-> func2, "c"-> func3)
...
val result = pipeline.foldLeft(initial_value) {case (acc,(key,func)) => func(acc)}
Get rid of your keys, first:
pipeline.values.foldLeft(initial_value)((a, f) => f.func(a))

Scala: Grouping list of tuples

I need to group list of tuples in some unique way.
For example, if I have
val l = List((1,2,3),(4,2,5),(2,3,3),(10,3,2))
Then I should group the list with second value and map with the set of first value
So the result should be
Map(2 -> Set(1,4), 3 -> Set(2,10))
By so far, I came up with this
l groupBy { p => p._2 } mapValues { v => (v map { vv => vv._1 }).toSet }
This works, but I believe there should be a much more efficient way...
This is similar to this question. Basically, as #serejja said, your approach is correct and also the most concise one. You could use collection.breakOut as builder factory argument to the last map and thereby save the additional iteration to get the Set type:
l.groupBy(_._2).mapValues(_.map(_._1)(collection.breakOut): Set[Int])
You shouldn't probably go beyond this, unless you really need to squeeze the performance.
Otherwise, this is how a general toMultiMap function could look like which allows you to control the values collection type:
import collection.generic.CanBuildFrom
import collection.mutable
def toMultiMap[A, K, V, Values](xs: TraversableOnce[A])
(key: A => K)(value: A => V)
(implicit cbfv: CanBuildFrom[Nothing, V, Values]): Map[K, Values] = {
val b = mutable.Map.empty[K, mutable.Builder[V, Values]]
xs.foreach { elem =>
b.getOrElseUpdate(key(elem), cbfv()) += value(elem)
}
b.map { case (k, vb) => (k, vb.result()) } (collection.breakOut)
}
What it does is, it uses a mutable Map during building stage, and values gathered in a mutable Builder first (the builder is provided by the CanBuildFrom instance). After the iteration over all input elements has completed, that mutable map of builder values is converted into an immutable map of the values collection type (again using the collection.breakOut trick to get the desired output collection straight away).
Ex:
val l = List((1,2,3),(4,2,5),(2,3,3),(10,3,2))
val v = toMultiMap(l)(_._2)(_._1) // uses Vector for values
val s: Map[Int, Set[Int] = toMultiMap(l)(_._2)(_._1) // uses Set for values
So your annotated result type directs the type inference of the values type. If you do not annotate the result, Scala will pick Vector as default collection type.

Iterate Over a tuple

I need to implement a generic method that takes a tuple and returns a Map
Example :
val tuple=((1,2),(("A","B"),("C",3)),4)
I have been trying to break this tuple into a list :
val list=tuple.productIterator.toList
Scala>list: List[Any] = List((1,2), ((A,B),(C,3)), 4)
But this way returns List[Any] .
I am trying now to find out how to iterate over the following tuple ,for example :
((1,2),(("A","B"),("C",3)),4)
in order to loop over each element 1,2,"A",B",...etc. How could I do this kind of iteration over the tuple
What about? :
def flatProduct(t: Product): Iterator[Any] = t.productIterator.flatMap {
case p: Product => flatProduct(p)
case x => Iterator(x)
}
val tuple = ((1,2),(("A","B"),("C",3)),4)
flatProduct(tuple).mkString(",") // 1,2,A,B,C,3,4
Ok, the Any-problem remains. At least that´s due to the return type of productIterator.
Instead of tuples, use Shapeless data structures like HList. You can have generic processing, and also don't lose type information.
The only problem is that documentation isn't very comprehensive.
tuple.productIterator map {
case (a,b) => println(a,b)
case (a) => println(a)
}
This works for me. tranform is a tuple consists of dataframes
def apply_function(a: DataFrame) = a.write.format("parquet").save("..." + a + ".parquet")
transform.productIterator.map(_.asInstanceOf[DataFrame]).foreach(a => apply_function(a))