List concatenation not working in scala - 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)

Related

Appending to a list in Scala

I have no clue why Scala decided to make this such a chore, but I simply want to add an item to a list
var previousIds: List[String] = List()
I have tried the following:
previousIds ::: List(dataListItem.id)
previousIds :: List(dataListItem.id)
previousIds :+ List(dataListItem.id)
previousIds +: List(dataListItem.id)
previousIds :+ dataListItem.id
previousIds +: dataListItem.id
In every one of these instances, the line will run but the list still will contain 0 items
When I try to add a new list:
val list = List[String](dataListItem.id)
previousIds += list
I get an error that list needs to be a string. When I add a string
previousIds += dataListItem.id
I get an error that it needs to be a list
For some reason, the only thing that will work is the following:
previousIds :::= List[String](dataListItem.id)
which seems really excessive, since, adding to a list should be a trivial option. I have no idea why nothing else works though.
How do you add an item to a list in scala (that already exists) without having to make a new list like I am doing?
Next code should help you for a start.
My assumption you are dealing with mutable collections:
val buf = scala.collection.mutable.ListBuffer.empty[String]
buf += "test"
buf.toList
In case you are dealing with immutable collections next approach would help:
val previousIds = List[String]("A", "TestB")
val newList = previousIds :: List("TestB")
Please refer to documentation for mode details:
http://www.scala-lang.org/api/current/scala/collection/immutable/List.html
Use MutableList
scala> var a = scala.collection.mutable.MutableList[String]()
a: scala.collection.mutable.MutableList[String] = MutableList()
scala> a += "s"
res0: scala.collection.mutable.MutableList[String] = MutableList(s)
scala> a :+= "s"
scala> a
res1: scala.collection.mutable.MutableList[String] = MutableList(s, s)

Scala list not adding elements

I am doing a sample program: adding a list of file names from a list of files. But I am getting an empty list after adding.
My code is this:
val regex = """(.*\.pdf$)|(.*\.doc$)""".r
val leftPath = "/Users/ravi/Documents/aa"
val leftFiles = recursiveListFiles(new File(leftPath), regex)
var leftFileNames = List[String]()
leftFiles.foreach((f:File) => {/*println(f.getName);*/ f.getName :: leftFileNames})
leftFileNames.foreach(println)
def recursiveListFiles(f: File, r: Regex): Array[File] = {
val these = f.listFiles
val good = these.filter(f => r.findFirstIn(f.getName).isDefined)
good ++ these.filter(_.isDirectory).flatMap(recursiveListFiles(_, r))
}
The last statement is not showing anything in the console.
f.getName :: leftFileNames means add the f.getName to the beginning of leftFileNames and return a new List, so it will not add into the leftFileNames. so for your example, you need to assign the leftFileNames after every operation, like:
leftFiles.foreach((f:File) => leftFileNames = f.getName :: leftFileNames)
but it's better not use the mutable variable in Scala, it's will cause the side effect, you can use map with reverse for this, like:
val leftFileNames = leftFiles.map(_.getName).reverse

Scala - Getting sequence id of elements during map

Scala newbie question.
I want to map a list to another list but I want to every object to know its sequence number.
In the following simple code, what is the right alternative to the usage of var v?
class T (s: String, val sequence:Int)
val stringList = List("a","b","C")
var v = 0
val tList = stringList.map(s => { v=v+1; new T(s, v);})
You can use zipWithIndex to get a tuple for each element containing the actual element and the index, then just map that tuple to your object:
List("a", "b", "C")
.zipWithIndex
.map(e => new T(e._1, e._2))
val tList = List.tabulate(stringList.length)(idx => new T(stringList(idx), idx))

Tune Nested Loop in Scala

I was wondering if I can tune the following Scala code :
def removeDuplicates(listOfTuple: List[(Class1,Class2)]): List[(Class1,Class2)] = {
var listNoDuplicates: List[(Class1, Class2)] = Nil
for (outerIndex <- 0 until listOfTuple.size) {
if (outerIndex != listOfTuple.size - 1)
for (innerIndex <- outerIndex + 1 until listOfTuple.size) {
if (listOfTuple(i)._1.flag.equals(listOfTuple(j)._1.flag))
listNoDuplicates = listOfTuple(i) :: listNoDuplicates
}
}
listNoDuplicates
}
Usually if you have someting looking like:
var accumulator: A = new A
for( b <- collection ) {
accumulator = update(accumulator, b)
}
val result = accumulator
can be converted in something like:
val result = collection.foldLeft( new A ){ (acc,b) => update( acc, b ) }
So here we can first use a map to force the unicity of flags. Supposing the flag has a type F:
val result = listOfTuples.foldLeft( Map[F,(ClassA,ClassB)] ){
( map, tuple ) => map + ( tuple._1.flag -> tuple )
}
Then the remaining tuples can be extracted from the map and converted to a list:
val uniqList = map.values.toList
It will keep the last tuple encoutered, if you want to keep the first one, replace foldLeft by foldRight, and invert the argument of the lambda.
Example:
case class ClassA( flag: Int )
case class ClassB( value: Int )
val listOfTuples =
List( (ClassA(1),ClassB(2)), (ClassA(3),ClassB(4)), (ClassA(1),ClassB(-1)) )
val result = listOfTuples.foldRight( Map[Int,(ClassA,ClassB)]() ) {
( tuple, map ) => map + ( tuple._1.flag -> tuple )
}
val uniqList = result.values.toList
//uniqList: List((ClassA(1),ClassB(2)), (ClassA(3),ClassB(4)))
Edit: If you need to retain the order of the initial list, use instead:
val uniqList = listOfTuples.filter( result.values.toSet )
This compiles, but as I can't test it it's hard to say if it does "The Right Thing" (tm):
def removeDuplicates(listOfTuple: List[(Class1,Class2)]): List[(Class1,Class2)] =
(for {outerIndex <- 0 until listOfTuple.size
if outerIndex != listOfTuple.size - 1
innerIndex <- outerIndex + 1 until listOfTuple.size
if listOfTuple(i)._1.flag == listOfTuple(j)._1.flag
} yield listOfTuple(i)).reverse.toList
Note that you can use == instead of equals (use eq if you need reference equality).
BTW: https://codereview.stackexchange.com/ is better suited for this type of question.
Do not use index with lists (like listOfTuple(i)). Index on lists have very lousy performance. So, some ways...
The easiest:
def removeDuplicates(listOfTuple: List[(Class1,Class2)]): List[(Class1,Class2)] =
SortedSet(listOfTuple: _*)(Ordering by (_._1.flag)).toList
This will preserve the last element of the list. If you want it to preserve the first element, pass listOfTuple.reverse instead. Because of the sorting, performance is, at best, O(nlogn). So, here's a faster way, using a mutable HashSet:
def removeDuplicates(listOfTuple: List[(Class1,Class2)]): List[(Class1,Class2)] = {
// Produce a hash map to find the duplicates
import scala.collection.mutable.HashSet
val seen = HashSet[Flag]()
// now fold
listOfTuple.foldLeft(Nil: List[(Class1,Class2)]) {
case (acc, el) =>
val result = if (seen(el._1.flag)) acc else el :: acc
seen += el._1.flag
result
}.reverse
}
One can avoid using a mutable HashSet in two ways:
Make seen a var, so that it can be updated.
Pass the set along with the list being created in the fold. The case then becomes:
case ((seen, acc), el) =>

In Scala, how to find an elemein in CSV by a pair of key values?

For example, from a following file:
Name,Surname,E-mail
John,Smith,john.smith#hotmail.com
Nancy,Smith,nancy.smith#gmail.com
Jane,Doe,jane.doe#aol.com
John,Doe,john.doe#yahoo.com
how do I get e-mail address of John Doe?
I use the following code now, but can specify only one key field now:
val src = Source.fromFile(file)
val iter = src.getLines().drop(1).map(_.split(","))
var quote = ""
iter.find( _(1) == "Doe" ) foreach (a => println(a(2)))
src.close()
I've tried writing "iter.find( _(0) == "John" && _(1) == "Doe" )", but this raises an error saying that only one parameter is expected (enclosing the condition into extra pair of parentheses does not help).
The underscore as a placeholder for a parameter to a lambda doesn't work the way that you think.
a => println(a)
// is equivalent to
println(_)
(a,b) => a + b
// is equivalent to
_ + _
a => a + a
// is not equivalent to
_ + _
That is, the first underscore means the first parameter and the second one means the second parameter and so on. So that's the reason for the error that you're seeing -- you're using two underscores but have only one parameter. The fix is to use the explicit version:
iter.find( a=> a(0) == "John" && a(1) == "Doe" )
You can use Regex:
scala> def getRegex(v1: String, v2: String) = (v1 + "," + v2 +",(\\S+)").r
getRegex: (v1: String,v2: String)scala.util.matching.Regex
scala> val src = """John,Smith,john.smith#hotmail.com
| Nancy,Smith,nancy.smith#gmail.com
| Jane,Doe,jane.doe#aol.com
| John,Doe,john.doe#yahoo.com
| """
src: java.lang.String =
John,Smith,john.smith#hotmail.com
Nancy,Smith,nancy.smith#gmail.com
Jane,Doe,jane.doe#aol.com
John,Doe,john.doe#yahoo.com
scala> val MAIL = getRegex("John","Doe")
MAIL: scala.util.matching.Regex = John,Doe,(\S+)
scala> val itr = src.lines
itr: Iterator[String] = non-empty iterator
scala> for(MAIL(address) <- itr) println(address)
john.doe#yahoo.com
scala>
You could also do a pattern match on the result of split in a for comprehension.
val firstName = "John"
val surName = "Doe"
val emails = for {
Array(`firstName`, `surName`, email) <-
src.getLines().drop(1) map { _ split ',' }
} yield { email }
println(emails.mkString(","))
Note the backticks in the pattern: this means we match on the value of firstName instead of introducing a new variable matching anything and shadowing the val firstname.