Java developers need to learn Coding Scala Way. Scala way of coding is quite different & you need to unlearn Java way of coding
Question: Given a list of numbers, how will you group the numbers by how many times they occur? for example output for “List(3, 3, 4, 5, 5, 5)” is List((3,2), (4,1), (5,3)).
Pre Java 8 way – imperative style
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; public class CountOccurrences { public static void main(String[] args) { List<Integer> input = Arrays.asList(3, 3, 4, 5, 5, 5); Map<Integer, Integer> occurrencesMap = new HashMap<Integer, Integer>(); for (Integer number : input) { if (occurrencesMap.containsKey(number)) { occurrencesMap.put(number, occurrencesMap.get(number) + 1); } else { occurrencesMap.put(number, 1); } } System.out.println(occurrencesMap); } } |
Output:
{3=2, 4=1, 5=3}
Java 8 way – Functional Programming (i.e FP) style
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.stream.Collectors; public class CountOccurrences { public static void main(String[] args) { List<Integer> input = Arrays.asList(3, 3, 4, 5, 5, 5); Map<Integer, Long> result = input.stream() .collect(Collectors.groupingBy(e -> e, Collectors.counting())); System.out.println(result); } } |
Output:
{3=2, 4=1, 5=3}
groupBy in Scala FP
Before we provide the solution, the Scala Way, “groupBy” function to the rescue. Here is an example:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
object Example1 extends App { val groupsOfFive = (1 to 22).toList.groupBy { case x if (x < 6) => "Group 1" case x if (x < 11) => "Group 2" case x if (x < 16) => "Group 3" case x if (x < 21) => "Group 4" case _ => "SOLD OUT" } //groupsOfFive is a Map[String, List[Int]] println(groupsOfFive); } |
Output:
1 2 3 |
Map(Group 4 -> List(16, 17, 18, 19, 20), SOLD OUT -> List(21, 22), Group 3 -> List(11, 12, 13, 14, 15), Group 2 -> List(6, 7, 8, 9, 10), Group 1 -> List(1, 2, 3, 4, 5)) |
Scala Way – FP style
1 2 3 4 5 6 7 8 9 10 11 12 |
object Example1 extends App { val input = List(3, 3, 4, 5, 5, 5); val result = input.groupBy { x => x }.map { case(num, times) => (num, times.size) }.toList.sortBy(_._1); println(result) } |
Output:
1 2 3 |
List((3,2), (4,1), (5,3)) |
Explanation:
Step 1: input.groupBy { x => x } returns a “Map[Int, List[Int]]”. In the above example it returns
1 2 3 |
Map(5 -> List(5, 5, 5), 4 -> List(4), 3 -> List(3, 3)) |
Step 2: The “num” is the Map “key” and “times” is the value.
1 2 3 4 5 |
val result = input.groupBy { x => x }.map { case(num, times) => (num, times.size) }.toList |
The above output is not sorted by “num”.
Step 3: Sort by “num”, which is the “first value” i.e. “_1” in the any given input i.e. “_”
1 2 3 4 5 |
val result = input.groupBy { x => x }.map { case (num, times) => (num, times.size) }.toList.sortBy(_._1); |
Scala Way 2: “identity” & “mapValues”
1 2 3 4 5 6 7 8 9 10 |
object Example1 extends App { val input = List(3, 3, 4, 5, 5, 5); val result = input.groupBy(identity).mapValues {_.size }.toList.sortBy(_._1); println(result) } |
The “scala.Predef” object has the following function
1 2 |
def identity[A](x: A): A |
Q: What does Predef.identity do in Scala?
A: “identity” simply returns its argument. It can be handy sometimes to pass to higher-order functions. In the above example,
instead of doing
1 2 3 |
input.groupBy { x => x } |
You can do
1 2 3 |
input.groupBy { identity } |
Finally, you can write the whole thing like a DSL (Domain Specific Language)
1 2 3 4 5 6 7 8 9 10 |
object Example1 extends App { val input = List(3, 3, 4, 5, 5, 5); val result = input groupBy identity mapValues (_.size) toList println(result.sortBy(_._1)) } |