2011-05-23 77 views
2

我对Scala很新,但从我读过的内容看来,它似乎是处理我正在处理的项目的理想语言。从列表中获得匹配谓词的子列表

我有一个非常大的CSV文件,该文件是这样的:

INDEX, CITY, COST 
    7 , London, 500 
    7 , Paris, 200 
    11 , Rome, 300 
    11 , New York, 100 
    11 , Madrid, 7 

我想在CSV文件的读取和生产都具有相同的指标,一个指标在时间元素的列表。

从上面的例子,我想获得包含行的列表:

7, London, 500 
7, Paris, 200 

和包含行的第二列表:

11, Rome, 300 
11, New York, 100 
11, Madrid, 7 

这是很容易在CSV文件中读取:

val iter = src.getLines().drop(1).map(_.split(",")) //from SO :) 

但是,我正在努力寻找一种干净的方式来生成我的子列表。在我看来,应该有一个很好,简洁的方式来实现这个使用Scala。由于有很多数据,我特别喜欢这些数据被延迟加载。你能建议我怎么做到这一点?

所有数据按索引排列(尽管索引不是顺序的),而且我正在使用的CSV文件不包含任何嵌套逗号或转义字符。

回答

2

Source.getLines已经很懒。它返回一个迭代器,它将根据需要从底层文件中提取每行。迭代器上的大多数操作也返回迭代器,因此在下面的代码中:

val iter = src.getLines.tail map {_ split ","} 

您正确命名了该值。它将是一个Iterator[Array[String]],每个String数组都是按需生成的。

你是否遇到过任何会暗示数据未被加载的问题?

更新子表

然后,为了产生一个从这个迭代器,您可以:

val id7 = iter filter {_(0) == 7) 

同样,这仍然会偷懒。

或者......你可以把这些很多:

val grouped = iter.toStream groupBy {_(0)} 

不幸的是,这并非完全懒惰。最后一行在第一列中可能具有唯一值,因此您需要从输入中读取每个元素以知道需要多少个细分。在REPL,它也更容易迫使子流,所以你可以看到它们包含的内容:

val grouped = iter.toStream groupBy {_(0)} mapValues {_.toList} 
+0

对不起,不,我没有遇到问题,这只是我想要的代码实现。我无法弄清楚如何按索引对行进行分组,本质上我想要一个Array [Array [String]]在索引列中将每行都用相同的值分组。 – Peter 2011-05-23 12:09:58

2
scala> List(Array(1,"a"),Array(2,"b"),Array(1,"c")).groupBy(_(0)) 
res1: scala.collection.immutable.Map[Any,List[Array[Any]]] = Map(1 -> List(Array(1, a), Array(1, c)), 2 -> List(Array(2, b))) 

所以,你必须做的是通过数组中的第一个元素添加.groupBy(_(0))到组。

+0

+1。不保留订单,但是OP没有要求。 – 2011-05-23 12:49:35

+1

OP * did *然而要求延迟加载,这个回答完全回避 – 2011-05-23 14:04:56

1

当你有很多数据时,你必须更加小心你想要做的操作。

让我们假设你的文件这样大,你不能把它全部加载到内存中,并以换取你愿意(被迫),以获得N不同读取它〜N倍子集。

首先,你应该弄清楚你需要多少个子集。让我们创建一个假装getLines方法:

val src = new { def getLines() = Iterator("#", "1,a", "2,b", "2,c") } 

现在我们需要找到所有初始索引。你可能使用split,但由于你正在处理大量的数据,并没有真正需要它的所有细分,让我们找到第一个逗号(这里假设总是有一个逗号可以找到):

val idx = Set() ++ src.getLines().drop(1).map(s => s.substring(0, s.indexOf(','))) 

好的,现在我们知道我们在找什么。然后,我们通过和得到它,带班的援助,这将有助于我们延迟加载的数据:

class OneIndex(index: String) { 
    lazy val data = src.getLines().drop(1).filter(
    s => index == s.substring(0,s.indexOf(',')) 
).toArray 
} 
val everything = idx.map(i => (i,new OneIndex(i))).toMap 

scala> everything("2").data.foreach(println) 
2,b 
2,c 

还有更多的事情之一可能加入 - 也许.toInt.trim.toInt将一些帮助点,将索引值从字符串转换为整数。有人可能会怀疑你是否真的需要延迟加载,因为它迫使你多次读取整个文件。但这至少是一个基本框架。