Dharmesh
Dharmesh

Reputation: 9

How to consecutive and non-consecutive list in scala?

val keywords = List("do", "abstract","if")

val resMap = io.Source
               .fromFile("src/demo/keyWord.txt")
               .getLines()
               .zipWithIndex
       .foldLeft(Map.empty[String,Seq[Int]].withDefaultValue(Seq.empty[Int])){
         case (m, (line, idx)) =>
           val subMap = line.split("\\W+")
                            .toSeq  //separate the words
                            .filter(keywords.contains)  //keep only key words
                            .groupBy(identity)  //make a Map w/ keyword as key
                            .mapValues(_.map(_ => idx+1))  //and List of line numbers as value
                            .withDefaultValue(Seq.empty[Int])
           keywords.map(kw => (kw, m(kw) ++ subMap(kw))).toMap
       }

println("keyword\t\tlines\t\tcount")
keywords.sorted.foreach{kw =>
  println(kw + "\t\t" +
          resMap(kw).distinct.mkString("[",",","]") + "\t\t" +
          resMap(kw).length)
}

This code is not mine and i don't own it ... .using for study purpose. However, I am still learning and I am stuck at implement consecutive to nonconsecutive list, such as the word "if" is in many line and when three or more consecutive line numbers appear then they should be written with a dash in between, e.g. 20-22, but not 20, 21, 22. How can I implement? I just wanted to learn this.

output:

keyword     lines       count
abstract    [1]         1
do          [6]         1
if      [14,15,16,17,18]    5

But I want the result to be such as [14-18] because word "if" is in line 14 to 18.

Upvotes: 0

Views: 334

Answers (2)

jwvh
jwvh

Reputation: 51271

Here's one way to go about it.

def collapseConsecutives(nums :Seq[Int]) :List[String] =
  nums.foldRight((nums.last, List.empty[List[Int]])) {
    case (n, (prev,acc)) if prev-n == 1 => (n, (n::acc.head) :: acc.tail)
    case (n, ( _  ,acc))                => (n, List(n) :: acc)
  }._2.map{ ns =>
    if (ns.length < 3) ns.mkString(",")  //1 or 2 non-collapsables
    else s"${ns.head}-${ns.last}"        //3 or more, collapsed
  }

usage:

println(kw + "\t\t" +
        collapseConsecutives(resMap(kw).distinct).mkString("[",",","]") + "\t\t" +
        resMap(kw).length)

Upvotes: 0

bebingando
bebingando

Reputation: 1022

First off, I'll give the customary caution that SO isn't meant to be a place to crowdsource answers to homework or projects. I'll give you the benefit of the doubt that this isn't the case.

That said, I hope you gain some understanding about breaking down this problem from this suggestion:

  1. your existing implementation has nothing in place to understand if the int values are indeed consecutive, so you are going to need to add some code that sorts the Ints returned from resMap(kw).distinct in order to set yourself up for the next steps. You can figure out how to do this.
  2. you will then need to group the Ints by their consecutive nature. For example, if you have (14,15,16,18,19,20,22) then this really needs to be further grouped into ((14,15,16),(18,19,20),(22)). You can come up with your algorithm for this.
  3. map over the outer collection (which is a Seq[Seq[Int]] at this point), having different handling depending on whether or not the length of the inside Seq is greater than 1. If greater than one, you can safely call head and tail to get the Ints that you need for rendering your range. Alternatively, you can more idiomatically make a for-comprehension that composes the values from headOption and tailOption to build the same range string. You said something about length of 3 in your question, so you can adjust this step to meet that need as necessary.
  4. lastly, now you have Seq[String] looking like ("14-16","18-20","22") that you need to join together using a mkString call similar to what you already have with the square brackets

For reference, you should get further acquainted with the Scaladoc for the Seq trait:

https://www.scala-lang.org/api/2.12.8/scala/collection/Seq.html

Upvotes: 1

Related Questions