15

I am a newbie to functional programming language and I am learning it in Scala for a University project.

This may seem simple, but I am unable to find enough help online for this or a straightforward way of doing this - how can I convert an Array[String] to Array[Double]? I have a CSV file which, when read into the REPL is interpreted as String values (each line of the file has a mix of integer and string values) which would return a type Array[String]. I want to encode the string values with a double/int values to return Array[Double] in order to make the array homogeneous. Is there a straightforward way of doing this? Any guidance will be much appreciated.

What I have done until now is:

def retrieveExamplesFromFile(fileName : String) : Array[Array[String]] = {
  val items = for {
    line <- Source.fromFile(fileName).getLines()
    entries = line.split(",")
    } yield entries

  return items.toArray
}

The format of each line (returned as String[]) is so:

[[1.0, 2.0, item1], [5, 8.9, item2],....]

And to convert each line in the CSV file into double array, I only have a psuedo definition drafted so:

def generateNumbersForStringValues(values : Array[String]) : Array[Double] = {
val line = for(item <- values)
{
    //correct way?
    item.replace("item1", "1.0")
    item.replace("item2", "1.0")    
} 
return //unable to typecast/convert
}

Any ideas are welcome. Thank you for your time.

4 Answers 4

29

You probably want to use map along with toDouble:

values.map(x => x.toDouble)

Or more concisely:

values.map(_.toDouble)

And for the fallback for non-double strings, you might consider using the Try monad (in scala.util):

values.map(x => Try(x.toDouble).getOrElse(1.0))

If you know what each line will look like, you could also do pattern matching:

values map { 
    case Array(a, b, c) => Array(a.toDouble, b.toDouble, 1.0)
}
Sign up to request clarification or add additional context in comments.

3 Comments

The getOrElse statement or pattern matching works the best for me. However, I wanted to know if I can add counter to this rather than returning just 1.0 all the time? I have heard of Range(n1, n2) but that is not what I am looking for, I am looking for a counter because I want item1 and item2 to have different (maybe consecutive) numbers. Is this possible?
Sure -- you can put whatever logic you want in the getOrElse clause. For example, you could make an iterator val fallbackIterator = Iterator.from(0), and then use getOrElse(fallbackIterator.next), so that each successive call to getOrElse will increment the fallback value.
I was looking for that Try monad: did not know in scala.util. v pretty answer here.
2

Expanding on @DaunnC's comment, you can use the Try utility to do this and pattern match on the result so you can avoid calling get or wrapping your result in an Option:

import scala.util.{Try, Success, Failure}

def main = {
  val maybeDoubles = Array("5", "1.0", "8.5", "10.0", "item1", "item2")

  val convertDoubles = maybeDoubles.map { x =>
    Try(x.toDouble)
  }

  val convertedArray = convertDoubles.map {
    _ match {
      case Success(res) => res
      case Failure(f) => 1.0
   }
  }

  convertedArray
}

This allows you to pattern match on the result of Try, which is always either a Success or Failure, without having to call get or otherwise wrap your results.

Here is some more information on Try courtesy of Mauricio Linhares: https://mauricio.github.io/2014/02/17/scala-either-try-and-the-m-word.html

2 Comments

Copy/pasted your code into my main method and I get this O/P: Success(5.0) Success(1.0) Success(8.5) Success(10.0) Failure(java.lang.NumberFormatException: For input string: "item1") Failure(java.lang.NumberFormatException: For input string: "item2")
Ah, that would be because it wasn't yielding the final map. I updated my answer.
2

You mean to convert all strings to double with a fallback to 1.0 for all inconvertible strings? That would be:

val x = Array(
  Array("1.0", "2.0", "item1"),
  Array("5", "8.9", "item2"))

x.map( _.map { y =>
  try {
    y.toDouble
  } catch {
    case _: NumberFormatException => 1.0
  }
})

3 Comments

better to catch the java.lang.NumberFormatException to avoid the compiler warning
@dhg and more importantly: to avoid catching fatal exceptions like OOM and internal VM errors.
isn't it better to use monad Try?
1

Scala 2.13 introduced String::toDoubleOption which used within a map transformation, can be associated with Option::getOrElse to safely cast Strings into Doubles:

Array("5", "1.0", ".", "8.5", "int").map(_.toDoubleOption.getOrElse(1d))
// Array[Double] = Array(5.0, 1.0, 1.0, 8.5, 1.0)

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.