Reputation: 1851
I am trying to learn functional programming in Scala. Right now I'm using the OOP way of having for loops to do a job. I have two lists userCurrentRole
and entitlements
over which I'm doing a double for loop:
for {
curr <- userCurrentRole {
ent <- entitlements
} {
if (ent.userEmail.split("@")(0) == curr.head) {
if (ent.roleName != curr(1)) {
grantOrRevoke += 1
grantList += SomeCaseClass(curr.head, ent.roleName)
}
}
}
}
Is it possible to convert this double for loop into a logic that uses map
or filter
or both or any functional programming features of scala, but without a for
loop?
EDIT 1: Added a list addition inside the double if..
Upvotes: 0
Views: 126
Reputation: 22850
Well, you are already using some higher order functions, only that you don't notice it, because you believe those are for loops, but they aren't loops. They are just sugar syntax for calls to map
& flatMap
. But in your case, also to foreach
and that plus mutability, is want doesn't make it functional.
I would recommend you to take a look to the scaladoc, you will find that collections have a lot of useful methods.
For example, in this case, we may use count
& sum
.
val grantOrRevoke = userCurrentRole.iterator.map {
// Maybe it would be better to have a list of tuples instead of a list of lists.
case List(username, userRole) =>
entitlements.count { ent =>
(ent.userEmail.split("@", 2)(0) == username) && (ent.roleName == userRole)
}
}.sum
Upvotes: 2
Reputation: 4608
The good news is: you are already using functional style! Since the for
is not a loop per se, but a "for comprehension", which desugars into flatMap
and map
calls. It's only easier to read / write.
However, the thing you should avoid is mutable variables, like the grantOrRevoke
thing you have there.
val revocations = for {
curr <- userCurrentRole {
ent <- entitlements
if ent.userEmail.split("@")(0) == curr.head
if ent.roleName != curr(1)
} yield {
1
}
revocations.size // same as revocations.sum
Note that the if
s inside the for block (usually) desugar to withFilter
calls, which is often preferable to filter
calls, since the latter builds up a new collection whereas the former avoids that.
Upvotes: 3
Reputation: 26056
You can write it like this:
val grantOrRevoke = userCurrentRole
.map(curr => entitlements
.filter(ent => ent.userEmail.split("@")(0) == curr.head && ent.roleName != curr(1))
.size)
.sum
Upvotes: 2