从列表中获取与谓词匹配的子列表

时间:2011-05-23 11:26:46

标签: scala list predicate

我对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文件不包含任何嵌套的逗号或转义。

3 个答案:

答案 0 :(得分:3)

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

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}

答案 1 :(得分: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))添加到组中。

答案 2 :(得分: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在某些时候会有所帮助,将索引值从字符串转换为整数。人们可能也想知道你是否真的想要延迟加载,因为它会强迫你多次读取整个文件。但这至少是一个基本框架。