找到最大值w.r.t。每组格式化字符串

时间:2018-05-23 12:49:45

标签: regex scala scala-collections

我正在努力寻找方案的解决方案。我在目录中有几个文件。让我们说

vbBaselIIIData_201802_3_d.data.20180405.txt.gz    
vbBaselIIIData_201802_4_d.data.20180405.txt.gz   
vbBaselIIIData_201803_4_d.data.20180405.txt.gz  
vbBaselIIIData_201803_5_d.data.20180405.txt.gz

这里假设第二个下划线后面的单个数字称为runnumber。我必须只选择最新runnumber的文件。所以在这种情况下,我只需要从四个文件中选择两个并将其放在一个可变的scala列表中。 ListBuffer应包含:

vbBaselIIIData_201802_4_d.data.20180405.txt.gz  
vbBaselIIIData_201803_5_d.data.20180405.txt.gz

有人可以建议我如何实现这一点。我正在使用Scala,但只有算法也受到赞赏。我们可以使用哪些正确的数据结构?我们需要实现哪些功能?任何建议。

3 个答案:

答案 0 :(得分:0)

如果您将文件名作为列表,例如:

val list = List("vbBaselIIIData_201802_3_d.data.20180405.txt.gz"   
, "vbBaselIIIData_201802_4_d.data.20180405.txt.gz"   
, "vbBaselIIIData_201803_4_d.data.20180405.txt.gz"  
, "vbBaselIIIData_201803_5_d.data.20180405.txt.gz")

然后你可以这样做:

list.map{f => 
  val s = f.split("_").toList
     (s(1), f)
   }.groupBy(_._1)
   .map(_._2.max)
   .values

返回:

MapLike.DefaultValuesIterable(vbBaselIIIData_201803_5_d.data.20180405.txt.gz, vbBaselIIIData_201802_4_d.data.20180405.txt.gz)

如你所愿。

答案 1 :(得分:0)

这是一个有希望的鼓舞人心的提案,展示了大量不同的语言功能和有用的收集方法:

val list = List(
  "vbBaselIIIData_201802_3_d.data.20180405.txt.gz",
  "vbBaselIIIData_201802_4_d.data.20180405.txt.gz",
  "vbBaselIIIData_201803_4_d.data.20180405.txt.gz",
  "vbBaselIIIData_201803_5_d.data.20180405.txt.gz"
)

val P = """[^_]+_(\d+)_(\d+)_.*""".r
val latest = list
  .map { str => {val P(id, run) = str; (str, id, run.toInt) }}
  .groupBy(_._2)                // group by id
  .mapValues(_.maxBy(_._3)._1)  // find the last run for each id
  .values                       // throw away the id
  .toList
  .sorted                       // restore ordering, mostly for cosmetic purposes

latest foreach println

简要说明您在阅读Scala简介时可能错过的非完全无关的部分:

  • "regex pattern".r将字符串转换为已编译的正则表达式
  • { stmt1 ; stmt2 ; stmt3 ; ... ; stmtN; result }计算到最后一个表达式result
  • Extractor语法可用于编译的正则表达式模式
  • val P(id, run) = str匹配第二个和第三个_ - 分隔值
  • _.maxBy(_._3)._1找到运行次数最多的三元组,然后再次提取第一个组件str

输出:

vbBaselIIIData_201802_4_d.data.20180405.txt.gz
vbBaselIIIData_201803_5_d.data.20180405.txt.gz

答案 2 :(得分:0)

即使你提到'算法',也不清楚你有什么性能需求。

如果没有更多特定需求,使用Scala的Collection API很容易做到这一点。即使您正在处理大型目录,也可以通过转移到Streams(至少在内存使用中)来实现一些良好的性能特征。

假设你有一个像def getFilesFromDir(path: String): List[String]这样的函数,其中List[String]是一个文件名列表,你需要做以下事情:

  • 按日期分组文件(List[String] => Map[String, List[String]]
  • 提取Runnumbers,保留原始文件名(List[String] => List[(String, Int)]
  • 选择最大运行次数(List[(String, Int)] => (String, Int)
  • 仅映射文件名((String, Int) => String
  • 仅选择生成的地图(Map[Date, String] => String
  • 的值

注意:如果你想进入纯粹的功能路线,你需要一个类似def getFilesFromDir(path: String): IO[List[String]]的功能)

使用Scala的Collections API,您可以通过以下方式实现上述目标:

def extractDate(fileName: String): String = ???
def extractRunnumber(fileName: String): String = ???

def getLatestRunnumbersFromDir(path: String): List[String] =
  getFilesFromDir(path)
    .groupBy(extractDate) // List[String] => Map[String, List[String]]
    .mapValues(selectMaxRunnumber) // Map[String, List[String]] => Map[String, String]
    .values // Map[String, String] => List[String]

def selectMaxRunnumber(fileNames: List[String]): String =
  fileNames.map(f => f -> extractRunnumber(f))
    .maxBy(p => p._2)
    ._1

我已将extractDateextractRunnumber实施留空。这些可以使用简单的正则表达式完成 - 如果您遇到问题,请告诉我。