J'ai du mal à trouver une solution pour un scénario. J'ai quelques fichiers dans un répertoire. Disons

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

Supposons ici que le numéro à un chiffre après le deuxième trait de soulignement s'appelle runnumber. Je dois choisir uniquement les fichiers avec le dernier numéro d'exécution. donc dans ce cas, je dois choisir seulement deux des quatre fichiers et les mettre dans une liste scala mutable. Le ListBuffer doit contenir:

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

Quelqu'un peut-il me suggérer comment mettre en œuvre cela. J'utilise Scala, mais seul l'algorithme est également apprécié. Quels pourraient être les bons ensembles de structures de données que nous pouvons utiliser? Quelles sont les fonctions que nous devons implémenter? Aucune suggestion.

-1
OBoy 23 mai 2018 à 15:49

3 réponses

Meilleure réponse

Voici une proposition, espérons-le quelque peu inspirante, qui démontre tout un tas de fonctionnalités de langage différentes et de méthodes utiles sur les collections:

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

Brève explication des parties pas entièrement triviales que vous avez peut-être manquées lors de la lecture d'une introduction à Scala:

  • "regex pattern".r convertit une chaîne en un modèle regex compilé
  • Un bloc { stmt1 ; stmt2 ; stmt3 ; ... ; stmtN; result } évalue jusqu'à la dernière expression result
  • La syntaxe de l'extracteur peut être utilisée pour les modèles de regex compilés
  • val P(id, run) = str correspond aux deuxième et troisième _ - valeurs séparées
  • _.maxBy(_._3)._1 trouve le triplet avec le numéro d'exécution le plus élevé, puis extrait à nouveau le premier composant str

Production:

vbBaselIIIData_201802_4_d.data.20180405.txt.gz
vbBaselIIIData_201803_5_d.data.20180405.txt.gz
1
Andrey Tyukin 23 mai 2018 à 16:40

Si vous avez les noms de fichiers sous forme de liste, comme:

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")

Ensuite, vous pouvez faire:

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

Cela renvoie:

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

Comme tu le voulais.

0
pme 23 mai 2018 à 13:34

Les besoins en performances dont vous avez besoin ne sont pas clairs, même si vous mentionnez un «algorithme».

Si vous n'avez pas de besoins plus spécifiques, une opération comme celle-ci est facile à faire avec l'API Collection de Scala. Même si vous avez affaire à d'énormes répertoires, vous pourriez probablement obtenir de bonnes caractéristiques de performances en passant à Streams (au moins en termes d'utilisation de la mémoire).

Donc, en supposant que vous ayez une fonction comme def getFilesFromDir(path: String): List[String] où le List[String] est une liste de noms de fichiers, vous devez faire ce qui suit:

  • Regrouper les fichiers par date (List[String] => Map[String, List[String]]
  • Extraire les numéros d'exécution, en conservant le nom de fichier d'origine (List[String] => List[(String, Int)])
  • Sélectionnez le numéro d'exécution maximum (List[(String, Int)] => (String, Int))
  • Mapper uniquement vers le nom de fichier ((String, Int) => String)
  • Sélectionnez uniquement les valeurs de la carte résultante (Map[Date, String] => String)

( Remarque : si vous voulez suivre la voie fonctionnelle pure, vous voudrez une fonction quelque chose comme def getFilesFromDir(path: String): IO[List[String]])

Avec l'API Collections de Scala, vous pouvez réaliser ce qui précède avec quelque chose comme ceci:

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

J'ai laissé les implémentations extractDate et extractRunnumber vides. Celles-ci peuvent être effectuées à l'aide d'expressions régulières simples - faites-moi savoir si vous rencontrez des problèmes avec cela.

0
Rajit 23 mai 2018 à 13:27