2011-05-23 36 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將一些幫助點,將索引值從字符串轉換爲整數。有人可能會懷疑你是否真的需要延遲加載,因爲它迫使你多次讀取整個文件。但這至少是一個基本框架。