Scala list not adding elements - scala

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

Related

How to create listBuffer in collect function

I tought that List is enough but I need to add element to my list.
I've tried to put this in ListBuffer constructor but without result.
var leavesValues: ListBuffer[Double] =
leaves
.collect { case leaf: Leaf => leaf.value.toDouble }
.toList
Later on I'm going to add value to my list so my expected output is mutable list.
Solution of Raman Mishra
But what if I need to append single value to the end of leavesValues
I can reverse but it's not good enough
I can use ListBuffer like below but I believe that there is cleaner solution:
val leavesValues: ListBuffer[Double] = ListBuffer()
leavesValues.appendAll(leaves
.collect { case leaf: Leaf => leaf.value.toDouble }
.toList)
case class Leaf(value:String)
val leaves = List(Leaf("5"), Leaf("6"), Leaf("7"), Leaf("8") ,Leaf("9") )
val leavesValues: List[Double] =
leaves
.collect { case leaf: Leaf => leaf.value.toDouble }
val value = Leaf("10").value.toDouble
val answer = value :: leavesValues
println(answer)
you can do it like this after getting the list of leavesValues you can prepand the value you want to add into the list.

How can I emit periodic results over an iteration?

I might have something like this:
val found = source.toCharArray.foreach{ c =>
// Process char c
// Sometimes (e.g. on newline) I want to emit a result to be
// captured in 'found'. There may be 0 or more captured results.
}
This shows my intent. I want to iterate over some collection of things. Whenever the need arrises I want to "emit" a result to be captured in found. It's not a direct 1-for-1 like map. collect() is a "pull", applying a partial function over the collection. I want a "push" behavior, where I visit everything but push out something when needed.
Is there a pattern or collection method I'm missing that does this?
Apparently, you have a Collection[Thing], and you want to obtain a new Collection[Event] by emitting a Collection[Event] for each Thing. That is, you want a function
(Collection[Thing], Thing => Collection[Event]) => Collection[Event]
That's exactly what flatMap does.
You can write it down with nested fors where the second generator defines what "events" have to be "emitted" for each input from the source. For example:
val input = "a2ba4b"
val result = (for {
c <- input
emitted <- {
if (c == 'a') List('A')
else if (c.isDigit) List.fill(c.toString.toInt)('|')
else Nil
}
} yield emitted).mkString
println(result)
prints
A||A||||
because each 'a' emits an 'A', each digit emits the right amount of tally marks, and all other symbols are ignored.
There are several other ways to express the same thing, for example, the above expression could also be rewritten with an explicit flatMap and with a pattern match instead of if-else:
println(input.flatMap{
case 'a' => "A"
case d if d.isDigit => "|" * (d.toString.toInt)
case _ => ""
})
I think you are looking for a way to build a Stream for your condition. Streams are lazy and are computed only when required.
val sourceString = "sdfdsdsfssd\ndfgdfgd\nsdfsfsggdfg\ndsgsfgdfgdfg\nsdfsffdg\nersdff\n"
val sourceStream = sourceString.toCharArray.toStream
def foundStreamCreator( source: Stream[Char], emmitBoundaryFunction: Char => Boolean): Stream[String] = {
def loop(sourceStream: Stream[Char], collector: List[Char]): Stream[String] =
sourceStream.isEmpty match {
case true => collector.mkString.reverse #:: Stream.empty[String]
case false => {
val char = sourceStream.head
emmitBoundaryFunction(char) match {
case true =>
collector.mkString.reverse #:: loop(sourceStream.tail, List.empty[Char])
case false =>
loop(sourceStream.tail, char :: collector)
}
}
}
loop(source, List.empty[Char])
}
val foundStream = foundStreamCreator(sourceStream, c => c == '\n')
val foundIterator = foundStream.toIterator
foundIterator.next()
// res0: String = sdfdsdsfssd
foundIterator.next()
// res1: String = dfgdfgd
foundIterator.next()
// res2: String = sdfsfsggdfg
It looks like foldLeft to me:
val found = ((List.empty[String], "") /: source.toCharArray) {case ((agg, tmp), char) =>
if (char == '\n') (tmp :: agg, "") // <- emit
else (agg, tmp + char)
}._1
Where you keep collecting items in a temporary location and then emit it when you run into a character signifying something. Since I used List you'll have to reverse at the end if you want it in order.

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)

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)

Converting MongoCursor to JSON

Using Casbah, I query Mongo.
val mongoClient = MongoClient("localhost", 27017)
val db = mongoClient("test")
val coll = db("test")
val results: MongoCursor = coll.find(builder)
var matchedDocuments = List[DBObject]()
for(result <- results) {
matchedDocuments = matchedDocuments :+ result
}
Then, I convert the List[DBObject] into JSON via:
val jsonString: String = buildJsonString(matchedDocuments)
Is there a better way to convert from "results" (MongoCursor) to JSON (JsValue)?
private def buildJsonString(list: List[DBObject]): Option[String] = {
def go(list: List[DBObject], json: String): Option[String] = list match {
case Nil => Some(json)
case x :: xs if(json == "") => go(xs, x.toString)
case x :: xs => go(xs, json + "," + x.toString)
case _ => None
}
go(list, "")
}
Assuming you want implicit conversion (like in flavian's answer), the easiest way to join the elements of your list with commas is:
private implicit def buildJsonString(list: List[DBObject]): String =
list.mkString(",")
Which is basically the answer given in Scala: join an iterable of strings
If you want to include the square brackets to properly construct a JSON array you'd just change it to:
list.mkString("[", ",", "]") // punctuation madness
However if you'd actually like to get to Play JsValue elements as you seem to indicate in the original question, then you could do:
list.map { x => Json.parse(x.toString) }
Which should produce a List[JsValue] instead of a String. However, if you're just going to convert it back to a string again when sending a response, then it's an unneeded step.