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

Find maximum w.r.t. substring within each group of formatted strings

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

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。我必须只选择具有最新运行号的文件。所以在这种情况下,我只需要从四个文件中选择两个并将其放入一个可变的 Scala 列表中。 ListBuffer 应包含:

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

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

如果您有文件名列表,例如:

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

这个returns:

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

如你所愿。

这是一个很有希望的鼓舞人心的提议,它展示了集合的一大堆不同的语言特性和有用的方法:

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
  • 提取器语法可用于已编译的正则表达式模式
  • 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

不清楚您有什么性能需求,即使您提到 'algorithm'。

如果您没有更具体的需求,使用 Scala 的 Collection API 可以很容易地做到这一点。即使您正在处理巨大的目录,您也可以通过迁移到 Streams 来获得一些良好的性能特征(至少在内存使用方面)。

所以假设你有一个类似 def getFilesFromDir(path: String): List[String] 的函数,其中 List[String] 是一个文件名列表,你需要执行以下操作:

  • 按日期分组文件(List[String] => Map[String, List[String]]
  • 提取 Runnumbers,保留原始文件名 (List[String] => List[(String, Int)])
  • Select 最大运行数 (List[(String, Int)] => (String, Int))
  • 仅映射到文件名 ((String, Int) => String)
  • Select 只是结果映射的值 (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 实现留空。这些可以使用简单的正则表达式来完成 - 如果您遇到问题,请告诉我。