我是Scala的新手,在工作时我发现需要映射日志文件中的一些数据。日志文件遵循此格式(值从原始值更改):
1343,37284.ab1-tbd,283
1344,37284.ab1-tbd,284
1345,37284.ab1-tbd,0
1346,28374.ab1-tbd,107
1347,28374.ab1-tbd,0
...
第一个数字并不重要,但第二个字段和第三个字段的数字部分是需要映射的数字。我需要地图的键对应于第二个字段的数字部分,该数字部分映射到其后面的每个第3个字段的列表。这是一个糟糕的解释,所以这里的例子是解析上面的日志后我需要的:
{
37284 => { 283, 284, 0 }
28374 => { 107, 0 }
}
我想出的解决方案是:
val data = for (line <- Source fromFile "path/to/log" getLines) yield line.split(',')
val ls = data.toList
val keys = ls.map(_(1).split('.')(0).toInt)
val vals = ls.map(_(2).toInt)
val keys2vals = for {
(k, v) <- (keys zip vals).groupBy(_._1)
list = v.map(_._2)
} yield (k, list)
在Scala中有更惯用的方法吗?这对我来说似乎有点尴尬和困惑。 (在解释时,请假设很少或根本没有语言功能的背景知识等。)另外,如果后来我想从映射中排除数字零,我该怎么做?
编辑:
此外,我如何将数据类似地转换为表格:
{
{ 37284, { 283 ,284, 0 } }
{ 28374, { 107, 0 } }
}
即。列表[(Int,List [Int])]? (此表单用于apache-spark的索引rdds)
答案 0 :(得分:0)
怎么样:
val assocList = for {
line <- Source.fromFile("path/to/log").getLines
Array(_, snd, thd) = line.split(',')
} yield (snd.split('.')(0).toInt, thd.toInt)
assocList.toList.groupBy(_._1).mapValues(_.map(_._2))
如果您想要List[(Int, List[Int])]
,请添加.toList
。
答案 1 :(得分:0)
我可能想用更少的线(也可以说更清晰)来写它:
${verNumber}
给出:
val l = List((1343,"37284.ab1-tbd",283),
(1344,"37284.ab1-tbd",284),
(1345,"37284.ab1-tbd",0),
(1346,"28374.ab1-tbd",107),
(1347,"28374.ab1-tbd",0))
// drop the unused data
val m = l.map(a => a._2.split('.')(0).toInt -> a._3)
// transform to Map of key -> matchedValues
m.groupBy(_._1) mapValues (_ map (_._2))
&#34;另外,如果稍后我想从映射中排除数字零,我该怎么做?&#34; - 您可以m: List[(Int, Int)] = List((37284,283), (37284,284), (37284,0), (28374,107), (28374,0))
res0: scala.collection.immutable.Map[Int,List[Int]] = Map(37284 -> List(283, 284, 0), 28374 -> List(107, 0))
初始列表:
filter
要转换为List [(Int,List [Int])],您只需要在生成的Map上调用.toList。
答案 2 :(得分:0)
val lines = io.Source.fromFile("path/to/log").getLines.toList
lines.map{x=>
val Array(_,second,_,fourth) = x.split("[,.]")
(second,fourth)
}.groupBy(_._1)
.mapValues(_.map(_._2))