Is this the scala programming style?

The scala version:

import scala.io.Source

val list = Source.fromFile("/etc/lsb-release").getLines().toList
val hash = list.map(_.split("=")).map {case Array(x,y) => (x,y)}.toMap
println(hash)

The ruby version:

hash = {}
File.read("/etc/lsb-release").each_line do |s|
  s.strip!
  (key,value) = s.split("=")
  hash[key] = value
end

p hash

Their output:

$ scala lsb.sc 
Map(DISTRIB_ID -> Ubuntu, DISTRIB_RELEASE -> 18.04, DISTRIB_CODENAME -> bionic, DISTRIB_DESCRIPTION -> "Ubuntu 18.04.6 LTS")

$ ruby lsb.rb 
{"DISTRIB_ID"=>"Ubuntu", "DISTRIB_RELEASE"=>"18.04", "DISTRIB_CODENAME"=>"bionic", "DISTRIB_DESCRIPTION"=>"\"Ubuntu 18.04.6 LTS\""}

May I ask:

  • is my scala code the scalish style?
  • I use two map() to prepare the data for toMap(), how can I run just one map() to get the same result? (maybe a regex for case capture?)

Thanks

Your code is fine, I am just going to improve a couple of minor details.

val hash = list.map(_.split('=').toList).collect { case x :: y :: Nil => (x,y) }.toMap

I prefer not to use Array and the collect ensures that if some line was wrong it will be just ignored instead of crashing the program.

Also, if you are in 2.13 you can use Using to ensure the file is closed.

import scala.util.Using

val hash = Using.resource(Source.fromFile("/etc/lsb-release")) { source =>
  source
    .getLines()
    .map(_.split('=').toList)
    .collect {
      case x :: y :: Nil => (x,y)
    }.toMap
}
1 Like

If you have Source.fromFile("/etc/lsb-release").getLines() without .toList you will get an Iterator. Then it won’t matter that you have two map calls because Iterator is lazy and will only iterate once over all lines when you call .toMap, instead of once per method call.

2 Likes

Thanks the code is graceful. I will check the Using way.

Thanks, I never know Iterator can map() .
Can all List() methods be used by Iterator?

Yes, but that comes from inheritance structure, that’s why scaladoc is invaluable

ok, difference is present between Iterator and Iterable(which contains iterator), but practically usage wise, similar, , (but iterator is part of lazy computation which is quite interesting in stream of data…)

I found groupBy is not a member of Iterator. :slight_smile:
I got the error:

error: value groupBy is not a member of Iterator[String]
did you mean grouped?

Hello

for Iterator, even there are two map() followed, why it just iterate the items only once? does the compiler combine the two map() into one?

thanks

The implementation of map on Iterator looks like this:

def map[B](f: A => B): Iterator[B] = new AbstractIterator[B] {
  override def knownSize = self.knownSize
  def hasNext = self.hasNext
  def next() = f(self.next())
}

So instead of iterating over all elements it just returns a new Iterator that will apply function f to every element that comes out of the first Iterator.

1 Like

Is not the compiler, is just the implementation. Iterators are lazy (as Jasper showed) thus all operations just create a new Iterator that knows how to process the elements once you start calling things like next

However, since they are mutable, it is not safe to share them. Because once you consume them once they are gone.

This is also the reason why they don’t have groupBy that won’t work since it would be impossible to create a Map[K, Iterator[V]] without those inner Iterators already being consumed.