Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to stay true to functional style in Scala for expressions

I've struggled to find a way to stay true to functional style in for expressions when I need to collect multiple parameters of an object into a List.

As an example, say I have a Notification object, which has both a fromId (the user id the notification is from) and an objectOwnerId (the id of the user who created the original object). These can differ in facebook style notifications ("X also commented on Y's post").

I can collect the userIds with a for expression like so

val userIds = for { notification <- notifications } yield notification.fromId

however say I want to collect both the fromIds and the objectOwnerIds into a single list, is there any way to do this in a single for expression without the user of vars?

I've done something like this in the past:

var ids = List()
for {
    notification <- notifications
    ids = ids ++ List(notification.fromId, notification.objectOwnerId)
}
ids = ids.distinct

but it feels like there must be a better way. The use of a var, and the need to call distinct after I complete the collection are both ugly. I could avoid the distinct with some conditionals, but I'm trying to learn the proper functional methods to do things.

Thanks in advance for any help!

like image 296
Kareem Avatar asked Dec 31 '11 19:12

Kareem


3 Answers

For such cases, there is foldLeft:

(notifications foldLeft Set.empty[Id]) { (set, notification) =>
  set ++ Seq(notification.fromId, notification.ownerId)
}

or in short form:

(Set.empty[Id] /: notifications) { (set, notification) =>
  set ++ Seq(notification.fromId, notification.ownerId)
}

A set doesn't hold duplicates. After the fold you can convert the set to another collection if you want.

like image 145
kiritsuku Avatar answered Nov 20 '22 13:11

kiritsuku


val userIds = for { 
  notification <- notifications 
  id <- List(notification.fromId, notification.objectOwnerId)
} yield id

Apply distinct afterwards if required. If the id can only be duplicated on a single notification, you can apply distinct on the second generator instead.

like image 21
Daniel C. Sobral Avatar answered Nov 20 '22 15:11

Daniel C. Sobral


Sure, instead of just yielding the fromId, yield a tuple

val idPairs:List[(String, String)] = for(notification <- notifications) yield(notification.fromId, notification.objectOwnerId)
like image 4
Dave Griffith Avatar answered Nov 20 '22 15:11

Dave Griffith