您需要兩個文件的intersection:來自file1和file2的共享一些條件的行。從集合論的角度考慮這個問題:你有兩組共同的元素,你需要一個新的元素。那麼,除此之外還有更多,因爲線條並不是真的相等......
因此,假設您讀取了file1,並且這是類型List[Input1]
。我們可以像這樣的代碼時,並沒有涉及到什麼Input1
任何細節是:
case class Input1(line: String)
val f1: List[Input1] = (Source fromFile "file1.csv" getLines() map Input1).toList
我們可以做同樣的事,文件2和List[Input2]
:
case class Input2(line: String)
val f2: List[Input2] = (Source fromFile "file2.csv" getLines() map Input2).toList
你可能會奇怪,爲什麼我創建了兩個不同的類如果他們有完全相同的定義。那麼,如果你正在閱讀結構化數據,那麼會有有兩種不同的類型,所以我們來看看如何處理更復雜的情況。
好了,我們如何配合他們,因爲Input1
和Input2
是不同的類型?那麼,這些行是通過鍵來匹配的,根據你的代碼,這些鍵是每一行中的第一列。因此,讓我們創建一個類Key
和轉化Input1 => Key
和Input2 => Key
:
case class Key(key: String)
def Input1IsKey(input: Input1): Key = Key(input.line split "," head) // using regex would be better
def Input2IsKey(input: Input2): Key = Key(input.line split "," head)
好了,現在我們可以生產從Input1
和Input2
共同Key
,讓我們得到他們的交集:
val intersection = (f1 map Input1IsKey).toSet intersect (f2 map Input2IsKey).toSet
所以我們可以建立我們想要的線,但我們沒有線!問題是,對於每個關鍵,我們需要知道它來自哪條線。考慮到我們有一組密鑰,並且對於每個密鑰,我們都想跟蹤一個值 - 這正是Map
!因此,我們可以建立這樣的:
val m1 = (f1 map (input => Input1IsKey(input) -> input)).toMap
val m2 = (f2 map (input => Input2IsKey(input) -> input)).toMap
所以輸出可以這樣產生:
val output = intersection map (key => m1(key).line + ", " + m2(key).line)
所有你現在要做的就是輸出。
讓我們考慮一下這段代碼的一些改進。首先,請注意,上面生成的輸出會重複鍵 - 這正是您的代碼所做的,但不是您在示例中想要的。讓我們改變,那麼,Input1
和Input2
從args來休息分裂的關鍵是:
case class Input1(key: String, rest: String)
case class Input2(key: String, rest: String)
它現在有點難以初始化F1和F2。而不是使用split
,這將會不必要地破壞所有的線路(並且性價比很高),我們會將線路劃分爲第一個逗號:之前的所有內容都是關鍵,之後的所有內容都是休息。該方法span
做的是:
def breakLine(line: String): (String, String) = line span (',' !=)
玩了一下與REPL的span
方法來更好地瞭解它。至於(',' !=)
,這只是(x => ',' != x)
的縮寫形式。
接下來,我們需要一種方法來創建一個元組Input1
和Input2
(的breakLine
結果):
def TupleIsInput1(tuple: (String, String)) = Input1(tuple._1, tuple._2)
def TupleIsInput2(tuple: (String, String)) = Input2(tuple._1, tuple._2)
現在我們可以讀取文件:
val f1: List[Input1] = (Source fromFile "file1.csv" getLines() map breakLine map TupleIsInput1).toList
val f2: List[Input2] = (Source fromFile "file2.csv" getLines() map breakLine map TupleIsInput2).toList
另一件事,我們可以簡化是交集。當我們創建一個Map
,其鍵是套,所以我們可以先創建地圖,然後用自己的鑰匙,從而計算交會:
case class Key(key: String)
def Input1IsKey(input: Input1): Key = Key(input.key)
def Input2IsKey(input: Input2): Key = Key(input.key)
// We now only keep the "rest" as the map value
val m1 = (f1 map (input => Input1IsKey(input) -> input.rest)).toMap
val m2 = (f2 map (input => Input2IsKey(input) -> input.rest)).toMap
val intersection = m1.keySet intersect m2.keySet
和輸出的計算是這樣的:
val output = intersection map (key => key + m1(key) + m2(key))
請注意,我不再附加逗號 - 其餘的f1和f2都以逗號開頭。
工程就像一個魅力,謝謝丹尼爾! – Jack
丹尼爾,我今天剛剛讀完這個答案,不得不說:「哇!它完美地解釋了所有事情「我知道這是兩年前的事情,但非常感謝您提供了這個偉大的答案,當時我真的幫助我理解了關於Scala的一大堆事情。 – Jack