I am new to scala. I need a lot of help with using maps and flatmaps with tuples.
I have functions as follows-
def extract(url: String): String = {//some code}
def splitstring(content: String): Array[String]={//some code}
def SentenceDetect(paragraph: Array[String]): Array[String] = {//some code}
def getMd5(literal: String): String = {//some code}
I have a incoming list of urls. and I want it to go through above series of functions using map and flatmaps.
var extracted_content=url_list.map(url => (url,extract(url)))
val trimmed_content=extracted_content.map(t => (t._1,splitstring(t._2)))
val sentences=trimmed_content.map(t => (t._1,SentenceDetect(t._2)))
val hashed_values=sentences.flatMap(t => (t._1,getMd5(t._2)))
The issue is I am getting at error at flatMap as type mismatch--
Error:(68, 46) type mismatch;
found : (String, String)
required: scala.collection.GenTraversableOnce[?]
val hashed_values=sentences.flatMap(t => (t._1,getMd5(t._2.toString)))
How to get it done.
I think this is what you're after.
val hashed_values = sentences.map(t => (t._1, t._2.map(getMd5)))
This should result in type List[(String,Array[String])]. This assumes that you actually want the Md5 calculation of each element in the t._2 array.
Recall that the signature of flatMap() is flatMap(f: (A) ⇒ GenTraversableOnce[B]), in other words, it takes a function that takes an element and returns a collection of transitioned elements. A tuple, (String,String), is not GenTraversableOnce thus the error you're getting.
You are getting this error because getMd5(...) accepts a string, however sentences is of type List[(String, Array[String])] (assuming url_list is List[String]), so t._2 is of type Array[String].
Anyway, some notes regarding your code:
variable names in scala are "lower camel" (https://docs.scala-lang.org/style/naming-conventions.html), not lower-with-underscore
extracted_content should probably be a val
since all your variables are maps, and since you want to transform the map's value, you better use .mapValues instead of .map
Related
I have one String list
val str = List("A","B","C","D")
and given:
val map = Map(("A"->3),("B"->1),("C"->10),("D"->5))
To sort str list based on given map value, I have tried str.sortBy(map). It's giving me error "A" key is not found. Could someone please help me out what am I doing wrong?
It should work as it is. Let's explain why. The signature of sortBy is:
def sortBy[B](f: A => B)(implicit ord: Ordering[B]): C = sorted(ord on f)
Therefore when you do str.sortBy(map), sortBy expects to get String => Int. str.sortBy(map) is equivalent to:
str.sortBy(s => map(s))
Note that Map extends MapOps(in Scala 2.13, in Scala 2.12 it is MapLike). MapOps(and MapLike) exposes an apply method, which takes (in your case) String and returns Int:
def apply(key: K): V = get(key) match {
case None => default(key)
case Some(value) => value
}
Hence writing str.sortBy(map) is the same as:
str.sortBy(s => map.apply(s))
which is the same as:
str.sortBy(map.apply)
Code run at Scastie.
I have a method
def foo(num: Int): String
Where I call some in some places in my code, and everything was good.
Lately, I encountered a situation where I need to call the same method, but with some parameter int value, I need to get in return 2 Strings, and not only one. My current way of implementing it is:
def foo(num: Int): List[String]
Where each time I call foo and get 1 String, I will get the head of the list, and each time I call for and it returns 2 strings, I will get the elements in [0, 1] (I know that when I call foo(10), I get 2 strings, and for the rest - only one).
Is there a more idiomatic scala/functional for this?
Scala 2.13 introduced literal-based singleton types, so actually you can do a crazy thing like this:
def foo(num: 10): (String, String) = ("Hello", "World")
def foo(num: Int): String = s"Hello $num"
val (left, right) = foo(10)
val single = foo(2)
and it will compile and work.
Of course, you can return a list instead of a tuple for the 10 case if you wish.
It should also work for typelevel scala (even before 2.13).
With regular Lightbend Scala before 2.13 you could still do that, but it was a lot clunkier. It was necessary to use an additional trick involving using type called Witness, but fortunately, it is provided by shapeless:
import shapeless.Witness
import shapeless.syntax.singleton._
def foo(num: Witness.`10`.T): (String, String) = ("Hello", "World")
def foo(num: Int): String = s"Hello $num"
val (left, right) = foo(10)
val single = foo(2)
And of course it is necessary to add shapeless as dependency:
libraryDependencies += "com.chuusai" %% "shapeless" % "2.3.3"
Another approach you could use is just the use of some kind of special container for your result. I would recommend tuple: (String, Option[String]). In case you're returning "regular" result, you would return (String, None) and in case of 10 you can return (String, Some[String]).
Since the result space is cleanly split into two sides (disjointed) consider Either like so
def foo(i: Int): Either[(String, String), String] =
if (i == 10) Left("one", "two") else Right("one")
val Left(Tuple2(x, y)) = foo(10)
val Right(a) = foo(2)
which is inspired by #Krzysztof's edit regarding "special container".
This question already has answers here:
Map versus FlatMap on String
(5 answers)
Closed 5 years ago.
Can anyone teach me property use cases of map and flatMap?
In Option case, I know these two methods have each signature, def map(A => B): Option[B] and def flatMap(A => Option[B]): Option[B].
So, I can get some value by two ways:
scala> val a = Some(1).map(_ + 2)
a: Option[Int] = Some(3)
scala> val a2 = Some(1).flatMap(n => Some(n + 2))
a2: Option[Int] = Some(3)
When I write a method: def plusTwo(n: Int), is there any difference between
def plusTwo(n: Int): Int = n + 2
Some(1).map(plusTwo)
and
def plusTwo(n: Int): Option[Int] = Some(n + 2)
Some(1).flatMap(plusTwo)
flatMap can convert to for-comprehension, and is it better that almost all methods return value Option wrapped?
Let's say you have a List:
val names = List("Benny", "Danna", "Tal")
names: List[String] = List(Benny, Danna, Tal)
Now let's go with your example. Say we have a function that returns an Option:
def f(name: String) = if (name contains "nn") Some(name) else None
The map function works by applying a function to each element in the list:
names.map(name => f(name))
List[Option[String]] = List(Some(Benny), Some(Danna), None)
In the other hand, flatMap applies a function that returns a sequence for each element in the list, and flattens the results into the original list
names.flatMap(name => f(name))
List[String] = List(Benny, Danna)
As you can see, the flatMap removed the Some/None layer and kept only the original list.
Your function plusTwo returns valid results for all input since you can add 2 to any Int.
There is no need to define that it returns Option[Int] because None value is never returned. That's why for such functions you use Option.map
But not all functions have meaningful result for every input. For example if your function divide some number by function parameter then it makes no sense to pass zero to that function.
Let's say we have a function:
def divideTenBy(a: Int): Double
When you invoke it with zero then ArithmeticException is thrown. Then you have to remember to catch this exception so it's better to make our function less error prone.
def divideTenBy(a: Int): Option[Double] = if (a == 0) None else Some(10 / a)
With such functions you can use flatMap since you can have 'None' value in optional (left operand) or given function can return None.
Now you can safely map this function on any value:
scala> None.flatMap(divideTenBy)
res9: Option[Double] = None
scala> Some(2).flatMap(divideTenBy)
res10: Option[Double] = Some(5.0)
scala> Some(0).flatMap(divideTenBy)
res11: Option[Double] = None
I am trying some basic logic using scala . I tried the below code but it throws error .
scala> val data = ("HI",List("HELLO","ARE"))
data: (String, List[String]) = (HI,List(HELLO, ARE))
scala> data.flatmap( elem => elem)
<console>:22: error: value flatmap is not a member of (String, List[String])
data.flatmap( elem => elem)
Expected Output :
(HI,HELLO,ARE)
Could some one help me to fix this issue?
You are trying to flatMap over a tuple, which won't work. The following will work:
val data = List(List("HI"),List("HELLO","ARE"))
val a = data.flatMap(x => x)
This will be very trivial in scala:
val data = ("HI",List("HELLO","ARE"))
println( data._1 :: data._2 )
what exact data structure are you working with?
If you are clear about you data structure:
type rec = (String, List[String])
val data : rec = ("HI",List("HELLO","ARE"))
val f = ( v: (String, List[String]) ) => v._1 :: v._2
f(data)
A couple of observations:
Currently there is no flatten method for tuples (unless you use shapeless).
flatMap cannot be directly applied to a list of elements which are a mix of elements and collections.
In your case, you can make element "HI" part of a List:
val data = List(List("HI"), List("HELLO","ARE"))
data.flatMap(identity)
Or, you can define a function to handle your mixed element types accordingly:
val data = List("HI", List("HELLO","ARE"))
def flatten(l: List[Any]): List[Any] = l.flatMap{
case x: List[_] => flatten(x)
case x => List(x)
}
flatten(data)
You are trying to flatMap on Tuple2 which is not available in current api
If you don't want to change your input, you can extract the values from Tuple2 and the extract the values for second tuple value as below
val data = ("HI",List("HELLO","ARE"))
val output = (data._1, data._2(0), data._2(1))
println(output)
If that's what you want:
val data = ("HI",List("HELLO,","ARE").mkString(""))
println(data)
>>(HI,HELLO,ARE)
I am using scalaz validation, and have some code to validate products.
def validateProduct(product: Option[Product]): ValidationNel[String, Product] = ???
Given a list of products, I want to get a single validation containing the whole list as a successful value or a list of validation errors. It seems like some kind of fold should do it, but I am not sure what the combination function should be.
def validateProducts(products: Seq[Option[Product]]): ValidationNel[String, Seq[Product]] = {
val listOfValidations: Seq[ValidationNel[String, Product]] = products.map(validateProduct _)
val validatedList:ValidationNel[Seq[String], Seq[Product]] = ??? // what to do here?
???
}
Any help is appreciated
If instead of a ValidationNel[List[String], List[Product]] you want a ValidationNel[String, List[Product]] (i.e., all the failures in the same list), you can just use traverse:
val result: ValidationNel[String, List[Product]] =
products.toList.traverseU(validateProduct)
Note that I've converted the Seq to a List as there are no type class instances for raw Seq, and I'm using traverseU rather than traverse as Scala's type inference doesn't quite work for non-trivial type constructors like ValidationNel
You can use fold with applicative
import scalaz.syntax.validation._
import scalaz.syntax.applicative._
case class Product(name: String)
val allGood = Seq(
Product("a").successNel[String],
Product("b").successNel[String]
)
val aggregated: ValidationNel[String, Seq[Product]] =
allGood.foldLeft(Seq.empty[Product].successNel[String]) {
case (acc , v) => (acc |#| v)(_ :+ _)
}
println(aggregated)