0
scala> val a  = jsonMap.get("L2_ID")
a: Option[Any] = Some(List(24493, 22774, 23609, 20517, 22829, 23646, 22779, 23578, 22765, 23657))

I want to fetch the first element of list i.e 24493. So, tried below code:

scala> var b = a.map(_.toString)
b: Option[String] = Some(List(24493, 22774, 23609, 20517, 22829, 23646, 22779, 23578, 22765, 23657))

scala>

scala>  var c = b.map(_.split(",")).toList.flatten
c: List[String] = List(List(24493, " 22774", " 23609", " 20517", " 22829", " 23646", " 22779", " 23578", " 22765", " 23657)")

scala> c(0)
res34: String = List(24493

This is not returning as expected.

bad_coder
  • 11,289
  • 20
  • 44
  • 72
Kamal Kishore
  • 325
  • 2
  • 4
  • 15

3 Answers3

4

I suggest you use pattern matching. To be defensive, i also added a Try to protect against the case of your json not being a List of numbers. Code below returns an Option[Int] and you can call .getOrElse(0) on it - or some other default value, if you like.

import scala.util.Try

val first = a match {
  case Some(h :: _) => Try(h.toString.toInt).toOption
  case _ => None
}
Philipp
  • 967
  • 6
  • 16
  • @KamalKishore If this answer solved your problem, then please mark the question as solved by [accepting this answer](https://meta.stackexchange.com/questions/5234/how-does-accepting-an-answer-work). – Andrey Tyukin Dec 26 '18 at 22:04
2

If you are certain that it's a Some, and that the list is non-empty, then you can unwrap the option and get the List[Int] using .get. Then you can access the first element of the list using .head:

val x: Option[List[Int]] = ???
x.get.head

If you are not in the REPL, and if you aren't sure whether it's a Some or None, and whether the List has any elements, then use

x.flatMap(_.headOption).getOrElse(yourDefaultValueEg0)

"Stringly-typed" programming is certainly not necessary in a language with such a powerful type system, so converting everything to string and splitting by commas was a seriously flawed approach.

Andrey Tyukin
  • 43,673
  • 4
  • 57
  • 93
  • this is not a good answer as it encourages unsafe patterns (`.get` or `.head` can throw on empty Option/Seq). Also, it doesn't work as you cannot `.headOption` on Any. – Philipp Dec 23 '18 at 08:15
  • @PhilippM Oh... Ok, admittedly, I missed the `Option[Any]` part. But I don't know where you detected any "encouragement" to use unsafe patterns: the entire second half of my answer shows how to do it properly with `flatMap` and `getOrElse`. It just would be unnecessary to write out `.flatMap...getOrElse` during an interactive REPL session, if one can already see that it's a `Some(List(34567, ...))`. – Andrey Tyukin Dec 23 '18 at 12:56
  • a.get.head is just bad style that leads to bugs, as my previous comment points out. I would not suggest this as a solution to someone new in the language. By starting your answer this way you do encourage the OP to use this API. The second half of your answer is also broken code. Maybe you could improve this by fixing the example and pointing out the pitfalls with the first half – Philipp Dec 23 '18 at 14:13
  • It does not lead to bugs. On input `val a = Some(List(24493))` it leads to `24493`, and to nothing else. Likewise, `Some(List(24493)).flatMap(_.headOption).getOrElse(42)` compiles and runs just fine, in what sense is it "broken"? – Andrey Tyukin Dec 23 '18 at 14:22
  • try None.get or Nil.head. yes these are bugs if the JSON object does not conform to the expected shape. If you take `a` as the OP has defined it then your code does not work. Please copy/paste this in a REPL to find out why, or, read my first comment on your answer again (Any dos not have .headOption defined). this will be my last comment on your answer, I am not here to teach you Scala, sorry. – Philipp Dec 23 '18 at 14:27
  • @PhilippM You are seeing someone trying to design a robust piece of software, who considers all the different cases, and tries to write "defensive" code. I see someone who just tried to extract an integer from a list using `.toString.split(",")` in a REPL session. These two views are irreconcilable. Point taken: `.get.head` would be code smell if it actually landed in any source code file. Your five-line-long attempt to extract an integer from a thing that's obviously some non-empty list would look like "Enterprise Quality FizzBuzz" in a throw-away interactive REPL session. – Andrey Tyukin Dec 23 '18 at 15:21
  • @PhilippM Since I have no way to find out whether the OP wanted something quick-and-dirty for the REPL session, or whether the OP wanted to use it in actual code, I propose that we agree to disagree on what would be the most appropriate solution, because the OP failed to describe the context clearly enough. – Andrey Tyukin Dec 23 '18 at 15:23
2

So, you have an Option, and List inside of it. Then scala> var b = a.map(_.toString) converts the contents of the Option (a List) into a String. That's not what you want. Look at the types of the results of your transformations, they are there to provide pretty good hints for you. b: Option[String], for example, tells you that you have lost the list ...

 a.map(_.map(_.toString))

has the type Option[List[String]] on the other hand: you have converted every element of the list to a string.

If you are just looking for the first element, there is no need to convert all of them though. Something like this will do:

 a
  .flatMap(_.headOption) // Option[Int], containing first element or None if list was empty or id a was None
  .map(_.toString) // convert Int inside of Option (if any) to String
  .getOrElse("")   // get the contents of the Option, or empty string if it was None
Dima
  • 39,570
  • 6
  • 44
  • 70