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

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.