user611105
user611105

Reputation:

Print the next X number of lines in Scala

Trying to learn Scala using the Programming in Scala book and they have a very basic example for reading lines from a file. I'm trying to expand on it and read a file line by line, look for a certain phrase, then print the next 6 lines following that line if it finds the line. I can write the script easily enough in something like java or Perl but I have no idea how to do it in Scala (probably because I'm not very familiar with the language yet...)

Here's the semi adapted sample code from the Programming in Scala book,

import scala.io.Source

if(args.length>0) {
    val lines = Source.fromFile(args(0)).getLines().toList
        for(line<-lines) {
            if(line.contains("secretPhrase")) {
                println(line)
                        //How to get the next lines here?   
            }
        }
}
else
Console.err.println("Pleaseenterfilename")

Upvotes: 2

Views: 1842

Answers (4)

jimbishopp
jimbishopp

Reputation: 527

Views are the secret sauce that makes iterating lists efficient in scala. Try this:

val l = Source.fromFile(args(0)).getLines().toList

l.view.zipWithIndex.filter(_._1.contains("secretPhrase")).foreach { 
  case (s, i) => l.view(i+1, i+7).foreach(println(_))
}

Upvotes: 2

Luigi Plinge
Luigi Plinge

Reputation: 51109

Using for on a collection just references that element, which is why you can't reference the following lines. If you need to reference the collection itself, there are a couple of ways:

1) use a built-in method. The appropriate one is tails. For instance, if our collection is List("a","b","c"),

scala> List("a","b","c").tails foreach println
List(a, b, c)
List(b, c)
List(c)
List()

This is almost what we need, except we don't want to check the empty List() at the end, so we can either a) put in a check that the list is not empty:

lines.tails foreach { xs => 
  if (!xs.isEmpty && (xs.head contains "secretPhrase")) 
    xs take 6 foreach println
}

or b) use init to take all elements except the last:

lines.tails.toList.init foreach { xs => 
  if (xs.head contains "secretPhrase") 
    xs take 6 foreach println
}

2) while loop / recursion.

a) while loop:

var xs = lines
while (!xs.isEmpty) {
  if (xs.head contains "secretPhrase") 
    xs take 6 foreach println
  xs = xs.tail
}

b) equivalent recursion

def print6(xs: List[String]): Unit = if (!xs.isEmpty) {
  if (xs.head contains "secretPhrase")
    xs take 6 foreach println
  print6(xs.tail)
}

print6(lines)

Upvotes: 1

Brian Smith
Brian Smith

Reputation: 3381

You can replace the for expression with something like this:

lines.sliding(7).filter(l => l(0).contains("secretPhrase")).foreach(println)

Note that this will miss the secret phrase if it's in the last 6 lines because of the way sliding works (it stops when it first encounters the end of the list).

You could work around this in a number of ways, the simplest perhaps being to append six dummy lines to the list before processing.

Upvotes: 2

Tesseract
Tesseract

Reputation: 8139

You could do it similar to how it's done in java

if(args.length > 0) {
  val lines = Source.fromFile(args(0)).getLines.toList
  for(i <- 0 until lines.size) {
    if(lines(i).contains("secretPhrase")) {
      for(j <- i+1 to i+6) println(lines(j))
    }
  }
}

or you use scala idioms to make it shorter

if(args.length > 0) {
  val lines = Source.fromFile(args(0)).getLines
  lines.dropWhile(!_.contains("secretPhrase"))
       .drop(1).take(6).foreach(println)
}

Upvotes: 8

Related Questions