[英]How to stay true to functional style in Scala for expressions
當我需要將一個對象的多個參數收集到一個列表中時,我一直在努力尋找一種方法來忠實於表達式的功能風格。
例如,假設我有一個Notification對象,它同時具有fromId(通知來自的用戶ID)和objectOwnerId(創建原始對象的用戶的ID)。 這些在Facebook樣式通知中可能會有所不同(“ X也評論了Y的帖子”)。
我可以使用for表達式收集userIds,如下所示
val userIds = for { notification <- notifications } yield notification.fromId
但是說我想將fromIds和objectOwnerIds都收集到一個列表中,有沒有辦法在沒有vars用戶的情況下在單個for表達式中做到這一點?
我過去做過這樣的事情:
var ids = List()
for {
notification <- notifications
ids = ids ++ List(notification.fromId, notification.objectOwnerId)
}
ids = ids.distinct
但感覺必須有更好的方法。 使用var以及在我完成收集后需要調用unique都是很丑的。 我可以通過一些條件來避免出現區別,但是我正在嘗試學習執行操作的正確函數方法。
在此先感謝您的幫助!
在這種情況下,存在foldLeft:
(notifications foldLeft Set.empty[Id]) { (set, notification) =>
set ++ Seq(notification.fromId, notification.ownerId)
}
或簡稱:
(Set.empty[Id] /: notifications) { (set, notification) =>
set ++ Seq(notification.fromId, notification.ownerId)
}
一組不包含重復項。 折疊后,您可以根據需要將集合轉換為另一個集合。
val userIds = for {
notification <- notifications
id <- List(notification.fromId, notification.objectOwnerId)
} yield id
如有需要,請在之后應用distinct
。 如果ID只能在單個通知被復制,你可以申請distinct
第二發生器來代替。
當然,不僅僅是產生fromId,而是產生一個元組
val idPairs:List[(String, String)] = for(notification <- notifications) yield(notification.fromId, notification.objectOwnerId)
好吧,這是我對以下內容的回答:
如何從[Y(x1,x2),Y(x3,x4)]映射到[x1,x2,x3,x4]?
使用flatMap
(請參閱Collection.Traversable ,但請注意,它實際上是在上flatMap
)。
case class Y(a: Int, b: Int)
var in = List(Y(1,2), Y(3,4))
var out = in.flatMap(x => List(x.a, x.b))
> defined class Y
> in: List[Y] = List(Y(1,2), Y(3,4))
> out: List[Int] = List(1, 2, 3, 4)
另外,因為for..yield
是filter
, map
和flatMap
的一個 (還請參見“ flatMap的糖嗎?” ,它指出效率不如可能:有一個額外的map
):
var out = for { i <- in; x <- Seq(i.a, i.b) } yield x
但是,我可能會選擇其他答案之一 ,因為這不能直接解決要解決的最終問題。
快樂的編碼。
您還可以使用Stream將對轉換為單個項目的流:
def toStream(xs: Iterable[Y]): Stream[Int] = {
xs match {
case Y(a, b) :: t => a #:: b #:: toStream(t)
case _ => Stream.empty
}
}
就像pst所說的那樣,這並不能解決您獲得唯一值的最終問題,但是一旦有了流,它就變得微不足道了:
val result = toStream(ys).toList.removeDuplicates
或對先前使用flatten的建議進行了一些修改-添加一個將Y轉換為List的函數:
def yToList(y: Y) = List(y.a, y.b)
然后,您可以執行以下操作:
val ys = List(Y(1, 2), Y(3, 4))
(ys map yToList flatten).removeDuplicates
我同意Dave的解決方案,但另一種方法是將其折疊在列表上,並隨即生成ID到User對象的映射。 折疊中要應用的功能將查詢兩個用戶的數據庫,並將它們添加到要累積的地圖中。
簡單map
呢? 無論如何, for yield
AFAIK都會轉換為一系列flatMap
和map
。 您的問題可以簡單地通過以下方式解決:
notifications.map(n => (n.fromId, n.objectOwnerId)).distinct
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.