[英]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.