0

I'm following the tutorial from Alvin Alexander to use Loan Pattern

Here is the code what I use -

val year = 2016
val nationalData = {
val source = io.Source.fromFile(s"resources/Babynames/names/yob$year.txt")
    // names is iterator of String, split() gives the array
    //.toArray & toSeq is a slow process compare to .toSet  // .toSeq gives Stream Closed error
    val names = source.getLines().filter(_.nonEmpty).map(_.split(",")(0)).toSet
    source.close()      
    names
    // println(names.mkString(","))     
}   
println("Names " + nationalData)

val info = for (stateFile <- new java.io.File("resources/Babynames/namesbystate").list(); if stateFile.endsWith(".TXT")) yield {
    val source = io.Source.fromFile("resources/Babynames/namesbystate/" + stateFile)
    val names = source.getLines().filter(_.nonEmpty).map(_.split(",")).
        filter(a => a(2).toInt == year).map(a => a(3)).toArray // .toSet        
    source.close()      
    (stateFile.take(2), names)      
}
    println(info(0)._2.size + " names from state "+ info(0)._1)
    println(info(1)._2.size + " names from state "+ info(1)._1)
    for ((state, sname) <- info) {
     println("State: " +state + " Coverage of name in "+ year+" "+ sname.count(n => nationalData.contains(n)).toDouble / nationalData.size) // Set doesn't have length method
}

This is how I applied readTextFile, readTextFileWithTry on the above code to learn/experiment Loan Pattern in the above code

def using[A <: { def close(): Unit }, B](resource: A)(f: A => B): B =
    try {
        f(resource)
    } finally {
        resource.close()
    }

def readTextFile(filename: String): Option[List[String]] = {
    try {
        val lines = using(fromFile(filename)) { source =>
            (for (line <- source.getLines) yield line).toList
        }
        Some(lines)
    } catch {
        case e: Exception => None
    }
}

def readTextFileWithTry(filename: String): Try[List[String]] = {
    Try {
        val lines = using(fromFile(filename)) { source =>
            (for (line <- source.getLines) yield line).toList
        }
        lines
    }
}

val year = 2016
val data = readTextFile(s"resources/Babynames/names/yob$year.txt") match {
    case Some(lines) =>
        val n = lines.filter(_.nonEmpty).map(_.split(",")(0)).toSet
        println(n)
    case None => println("couldn't read file")
}

val data1 = readTextFileWithTry("resources/Babynames/namesbystate")
data1 match {
    case Success(lines) => {
        val info = for (stateFile <- data1; if stateFile.endsWith(".TXT")) yield {
            val source = fromFile("resources/Babynames/namesbystate/" + stateFile)
            val names = source.getLines().filter(_.nonEmpty).map(_.split(",")).
                filter(a => a(2).toInt == year).map(a => a(3)).toArray // .toSet
            (stateFile.take(2), names)
            println(names)
        }
    }

But in the second case, readTextFileWithTry, I am getting the following error -

Failed, message is: java.io.FileNotFoundException: resources\Babynames\namesbystate (Access is denied)

I guess the reason for the failure is from SO what I understand - I am trying to open the same file on each iteration of the for loop

Apart from that, I have few concerns regarding how I use -

  • Is it the good way to use? Can some help me how can I use the TRY on multiple occasions?
  • I tried to change the return type of readTextFileWithTry like Option[A] or Set/Map or Scala Collection to apply higher-order functions later on that. but not able to succeed. Not sure that is a good practice or not.
  • How can I use higher-order functions in Success case, as there are multiple operations and in Success case the code blocks get bigger? I can't use any field outside of Success case.

Can someone help me to understand?

royki
  • 1,593
  • 3
  • 25
  • 45

1 Answers1

0

I think that you problem has nothing to do with "I am trying to open the same file on each iteration of the for loop" and it is actually the same as in the accepted answer

Unfortunately you didn't provide stack trace so it is not clear on which line this happens. I would guess that the falling call is

val data1 = readTextFileWithTry("resources/Babynames/namesbystate")

And looking at your first code sample:

val info = for (stateFile <- new java.io.File("resources/Babynames/namesbystate").list(); if stateFile.endsWith(".TXT")) yield {

it looks like the path "resources/Babynames/namesbystate" points to a directory. But in your second example you are trying to read it as a file and this is the reason for the error. It comes from the fact that your readTextFileWithTry is not a valid substitute for java.io.File.list call. And File.list doesn't need a wrapper because it doesn't use any intermediate closeable/disposable entity.

P.S. it might make more sense to use File.list(FilenameFilter filter) instead of if stateFile.endsWith(".TXT"))

SergGr
  • 23,570
  • 2
  • 30
  • 51