2016-03-10 62 views
1

有幾個庫,如Spark和其他Scala擴展具有「groupWith」功能。此函數允許您將元素與集合的其餘部分進行比較,然後使用一個或多個謂詞對其進行分組。在Scala中似乎沒有任何本地功能,但它們具有sortWith功能,其行爲相似,但只對項目進行排序而不是對它們進行分組。如果解釋不充分這裏應該顯示一個小代碼示例我想要做的事:是否有像sortWith函數一樣的本地分組函數?

val list = List(1,2,3,4,5,5) 
val groupedList = list.groupWith{ (e,c) => 
    e == c 
} 

這是一個非常簡單的例子,我想要做更復雜的比較,如

e + 1 == c 

所以問題是,是否有任何原生Scala函數可以做到這一點?任何建議或解決方法?

更新: 從簡單的例子給了它似乎它不完全清楚我想要做的,這裏有一個更好的例子: 說我有一個案例類和這些對象的列表:

case class Item(num: Int, color: String) 
val list = List(new Item(13, "red"), new Item(14,"red"), new Item(15, "blue"), new Item(16, "red")) 

list.groupWith{ (e,c) => 
    (e.num -1 == c.num || e.num + 1 == c.num) && e.color == c.color   
} 

,應返回是這樣的:

res8: List[List[Item]] = List(List(Item(13,red), Item(14,red)), List(Item(15,blue)), List(Item(16,red))) 
+1

CA你告訴我們,那些不熟悉瓦特/星火groupWith,你的結果在'groupedList'這兩種情況? – Teliatko

+0

我錯過了'groupWith'的語義。 – Teliatko

+0

你是否希望按照順序對(即(1,3,2,3)和(e,e + 1))進行組合,如果它返回(1,2),(2,3),(2, 3)還是僅僅(2,3)? –

回答

2

下面是一個實現:

// Takes the list as a parameter, can use pimp-my-library if you want 
def groupWith[A](xs: List[A], f: (A, A) => Boolean) = { 
    // helper function to add "e" to any list with a member that matches the predicate 
    // otherwise add it to a list of its own 
    def addtoGroup(gs: List[List[A]], e: A): List[List[A]] = { 
    val (before, after) = gs.span(_.exists(!f(_, e))) 
    if (after.isEmpty) 
     List(e) :: gs 
    else 
     before ::: (e :: after.head) :: after.tail 
    } 
    // now a simple foldLeft adding each element to the appropriate list 
    xs.foldLeft(Nil: List[List[A]])(addtoGroup) 
} 

groupWith(list, { (e: Item, c: Item) => 
        (e.num - 1 == c.num || e.num + 1 == c.num) && e.color == c.color}) 

//| res0: List[List[groups.groups.Item]] = 
//   List(List(Item(16,red)), 
//    List(Item(15 ,blue)), 
//    List(Item(14,red), Item(13,red))) 
+0

這種方法絕對適合描述函數需要做的事情,但我一直在尋找一些本地於Scala的東西(即我不需要自己寫的東西)。 – goodOldFashioned

+1

好吧,你不需要自己寫。我爲你寫的:)據我所知,標準庫中沒有任何內容完全符合你的要求。 –

+0

沒錯,我想這本身就能回答我的問題,而且這也是我要找的答案,謝謝! – goodOldFashioned

1

不知道這是否你想要什麼(檢查我的意見,你的問題),但有方法在GenTraversableLike中定義的,其中List繼承(不僅是List)。您將獲得:

scala> val list = List(1,2,3,4,5,5) 
list: List[Int] = List(1, 2, 3, 4, 5, 5) 

scala> list.groupBy(el => el) 
res0: scala.collection.immutable.Map[Int,List[Int]] = Map(5 -> List(5, 5), 1 -> List(1), 2 -> List(2), 3 -> List(3), 4 -> List(4)) 

scala> list.groupBy(el => el + 1) 
res1: scala.collection.immutable.Map[Int,List[Int]] = Map(5 -> List(4), 6 -> List(5, 5), 2 -> List(1), 3 -> List(2), 4 -> List(3)) 

基本上你需要提供從值判定功能鍵,你會得到Map[Key, List[Value]

這是你想要的嗎?

+0

不完全,我知道groupBy函數,但它只允許您與元素本身進行比較,所以根據數字接近進行分組操作是不可能的(我認爲)。查看我的更新,瞭解我所需要的更清晰的圖片。 – goodOldFashioned

相關問題