Diffrence between "for (elm <- myList) yield f(_)" and "myList map f(_)" in Scala - scala

Why does the different lines give different return values?
val tagIds = postData._1 map (TagTable.newTag(_))
// tagIds is defined as val tagIds: Array[Long]
and
val tagIds = for(tag <- postData._1) yield TagTable.newTag(_)
// tagIds is defined as val tagIds: Array[models.UnsavedTag => Long]

Due to a simple typing error:
val tagIds = for(tag <- postData._1) yield TagTable.newTag(tag)
^^^

val tagIds = postData._1 map (TagTable.newTag(_))
This line says take each item tag contained in the collection postData._1 and call TagTable.newTag(tag). Then, tagIds is a collection containing all of the results of those calls.
val tagIds = for(tag <- postData._1) yield TagTable.newTag(_)
This line says for each item tag contained in the collection postData._1, return the function TagTable.newTag(_) (which is equivalent to the function x => TagTable.newTag(x)). Then, tagIds is a collection containing all of those functions.
Basically, you aren't actually calling the function in the second version. Change it to this:
val tagIds = for(tag <- postData._1) yield TagTable.newTag(tag)

Related

List concatenation not working in scala

I am trying to concatenate scala list in loop using below code.
var names: List[String] = Nil
val cluster_id = List("149095311_0", "149095311_1")
for (id <- cluster_id) {
val influencers_name = searchIndex(s"id : $id", "id", "influencers", searcher)
println("In Loop " + influencers_name)
names :::= influencers_name
}
for(n <- names) println("List element -> " + n)
But when I iterate over final list it give me individual list's instead of individual elements of concatenated List.
Below is the O/P of above code:
In Loop List(kroger 10TV DispatchAlerts)
In Loop List(kroger seanhannity SenTedCruz)
List element -> kroger seanhannity SenTedCruz
List element -> kroger 10TV DispatchAlerts
Your code isn't very functional in that you are mutating variables. The following is more elegant:
def searchIndex(s: String): List[String] = {
if (s == "149095311_0") List("kroger 10TV DispatchAlerts")
else List("kroger seanhannity SenTedCruz")
}
val cluster_id = List("149095311_0", "149095311_1")
val names = cluster_id.foldLeft(List[String]()) {
(acc, id) => acc ++ searchIndex(id)
}
for(n <- names) println("List element -> " + n)
Where '++' is used to concatenate the elements of two lists.
The reason is,
When you do names ::: List("anything") -> it does not add anything to names.
Instead, it creates a new collection.
For example,
scala> var names: List[String] = Nil
names: List[String] = List()
scala> names ::: List("mahesh")
res0: List[String] = List(mahesh)
You can achive that
scala> names ::: List("chand")
res1: List[String] = List(chand)
scala> res0 ::: List("chand")
res2: List[String] = List(mahesh, chand)
When I added "Mahesh" to it, it has created new collection naming res0.
When I added again different string, here "chand" it has created another collection. But when I added "chand" to the created collection, it has concatenated to correct collection,
You can achive What you want to do,
scala> for(i <- List("a" ,"b" )){
| names = i :: names }
scala> names
res11: List[String] = List(b, a)
It looks like the problem is in searchIndex method that is retreiving a List[String] with a single String that contain all the values separated by a space, fix that method to make sure that it retrieves a List with one elemente per value.
To check if this is right try this, this is just a workaround, you should fix searchIndex
var names: List[String] = Nil
val cluster_id = List("149095311_0", "149095311_1")
for (id <- cluster_id) {
val influencers_name = searchIndex(s"id : $id", "id", "influencers", searcher).flatMap(_.split(' '))
("In Loop " + influencers_name)
names = influencers_name ::: names
}
for(n <- names) println("List element -> " + n)

Do Aggregation with Slick

My database structure looks like this:
id | content
I what to get the entry with max id (not just id).
I read the answer How to make aggregations with slick, but I found there is no first method in the statement: Query(Coffees.map(_.price).max).first. How to do that now?
What if I need the content of the item with the max id?
To retrieve another column, you could do something like the following. The below example calculates the max of one column, finds the row with that maximum value, and returns the value of another column in that row:
val coffees = TableQuery[Coffees]
val mostExpensiveCoffeeQuery =
for {
maxPrice <- coffees.map(_.price).max.result
c <- maxPrice match {
case Some(p) => coffees.filter(_.price === p).result
case None => DBIO.successful(Seq())
}
} yield c.headOption.map(_.name)
val mostExpensiveCoffee = db.run(mostExpensiveCoffeeQuery)
// Future[Option[String]]
Alternatively, to return a full Coffees object:
val mostExpensiveCoffeeQuery =
for {
...
} yield c.headOption
val mostExpensiveCoffee = db.run(mostExpensiveCoffeeQuery)
// Future[Option[Coffees]]

Creating a Map by reading elements of List in Scala

I have some records in a List .
Now I want to create a new Map(Mutable Map) from that List with unique key for each record. I want to achieve this my reading a List and calling the higher order method called map in scala.
records.txt is my input file
100,Surender,2015-01-27
100,Surender,2015-01-30
101,Raja,2015-02-19
Expected Output :
Map(0-> 100,Surender,2015-01-27, 1 -> 100,Surender,2015-01-30,2 ->101,Raja,2015-02-19)
Scala Code :
object SampleObject{
def main(args:Array[String]) ={
val mutableMap = scala.collection.mutable.Map[Int,String]()
var i:Int =0
val myList=Source.fromFile("D:\\Scala_inputfiles\\records.txt").getLines().toList;
println(myList)
val resultList= myList.map { x =>
{
mutableMap(i) =x.toString()
i=i+1
}
}
println(mutableMap)
}
}
But I am getting output like below
Map(1 -> 101,Raja,2015-02-19)
I want to understand why it is keeping the last record alone .
Could some one help me?
val mm: Map[Int, String] = Source.fromFile(filename).getLines
.zipWithIndex
.map({ case (line, i) => i -> line })(collection.breakOut)
Here the (collection.breakOut) is to avoid the extra parse caused by toMap.
Consider
(for {
(line, i) <- Source.fromFile(filename).getLines.zipWithIndex
} yield i -> line).toMap
where we read each line, associate an index value starting from zero and create a map out of each association.

Create Map in Scala using loop

I am trying to create a map after getting result for each items in the list. Here is what I tried so far:
val sourceList: List[(Int, Int)] = ....
val resultMap: Map[Int, Int] = for(srcItem <- sourceList) {
val result: Int = someFunction(srcItem._1)
Map(srcItem._1 -> result)
}
But I am getting type mismatch error in IntelliJ and I am definitely not writing proper syntax here. I don't think I can use yield as I don't want List of Map. What is correct way to create Map using for loop. Any suggestion?
The simplest way is to create the map out of a list of tuples:
val resultMap = sourceList.map(item => (item._1, someFunction(item._1))).toMap
Or, in the monadic way:
val listOfTuples = for {
(value, _) <- sourceList
} yield (value, someFunction(value))
val resultMap = listOfTuples.toMap
Alternatively, if you want to avoid the creation of listOfTuples you can make the transformation a lazy one by calling .view on sourceList and then call toMap:
val resultMap = sourceList.view
.map(item => (item._1, someFunction(item._1)))
.toMap
Finally, if you really want to avoid generating extra objects you can use a mutable Map instead and append the keys and values to it using += or .put

SLICK: How to use query result in another query?

I'd like to perform something like the following:
I'd like to return a list of users sorted first by who the user is "following", second by some additional point score.
The following code below which I wrote however doesn't work because the funder is the lifted Slick type and therefore is never found in the List.
//The following represents the query for only funders who we are following
val following_funders: List[User] = (
for {
funder <- all_funders
f <- follows if f.followerId === id //get all the current users follower objects
if f.followeeId === funder.id
} yield funder
).list
val all_funders_sorted = for {
funder <- all_funders
following_funder = following_funders contains funder
} yield (funder, following_funder)
//sort the funders by whether or not they are following the funder and then map it to only the funders (i.e. remove the boolean)
all_funders_sorted.sortBy(_._2.desc).sortBy(_._1.score.desc).map( x => x._1 )
All help appreciated!
You need to work with ids (i.e. primary keys) in Slick. That's how objects are uniquely identified on the db side. You do not need to execute the first query. You can use it as a component of your second without executing it first using the in operator:
//The following represents the query for only funders who we are following
val following_funders_ids = (
for {
funder <- all_funders
f <- follows if f.followerId === id //get all the current users follower objects
if f.followeeId === funder.id
} yield funder.id
val all_funders_sorted = for {
funder <- all_funders
following_funder = funder.id in following_funders_ids
} yield (funder, following_funder)
//sort the funders by whether or not they are following the funder and then map it to only the funders (i.e. remove the boolean)
all_funders_sorted.sortBy(_._1.impactPoints.desc).sortBy(_._2.desc).map( x => x._1 )
Be aware that your sort order was wrong, if you first want to sort by following. Slick translates .sortBy(_.a).sortBy(_.b) to ORDER BY B,A because that's how Scala collections work:
scala> List( (1,"b"), (2,"a") ).sortBy(_._1).sortBy(_._2)
res0: List[(Int, String)] = List((2,a), (1,b))
Ended up figuring it out the following way by using 'inSet'
//The following represents the query for only funders who we are following
val following_funders_ids: List[Long] = (
for {
funder <- all_funders
f <- follows if f.followerId === id //get all the current users follower objects
if f.followeeId === funder.id
} yield funder.id
).list
val all_funders_sorted = for {
funder <- all_funders
following_funder = funder.id inSet following_funders_ids
} yield (funder, following_funder)
//sort the funders by whether or not they are following the funder and then map it to only the funders (i.e. remove the boolean)
all_funders_sorted.sortBy(_._2.desc).sortBy(_._1.impactPoints.desc).map( x => x._1 )