Reputation: 9
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
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
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:
Int
s returned from resMap(kw).distinct
in order to set yourself up for the next steps. You can figure out how to do this.Int
s 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.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 Int
s 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.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 bracketsFor 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