This is one of the commonly asked questions in a coding interview. The solutions which I could find on most of the popular sites were not utilizing the Java 8 features. so here is a solution for this problem using Streams and Collectors.
the input would be
String input = "bookkeeper";
Step 1: Split the input string to get each letters using input.split("")
Step 1: To ignore the case sensitivity we’ll use input.toLowerCase().split("")
Step 3: using the terminal operator collect
and Collectors.groupingBy(e -> e, Collectors.counting())
we’ll reduce the stream to a Map with key as String and frequency as Long.
Step 4: Get the maps entrySet as stream .entrySet().stream()
Step 5: Map the entry to the desired output format and print each using a forEach method.
Stream.of(input.toLowerCase().split(""))
.collect(Collectors.groupingBy(e -> e, Collectors.counting()))
.entrySet()
.stream()
.map(e -> e.getKey()+e.getValue()+" ")
.forEach(System.out::print);
Output: p1 r1 b1 e3 k2 o2
HashMap doesn’t preserve the order in which key is added. To preserve the order we need to use LinkedHashMap
.
Stream.of(input.toLowerCase().split(""))
.collect(Collectors.groupingBy(e -> e, LinkedHashMap::new, Collectors.counting()))
.entrySet()
.stream()
.map(e -> e.getKey()+e.getValue()+" ")
.forEach(System.out::print);
Output: b1 o2 k2 e3 p1 r1
Sorting the stream based on the key of map Map.Entry.comparingByKey()
.
Note that this could be done by using just sort()
after .map
operation as the map operation would generate string.
Stream.of(input.toLowerCase().split(""))
.collect(Collectors.groupingBy(e -> e, Collectors.counting()))
.entrySet()
.stream()
.sorted(Map.Entry.comparingByKey())
.map(e -> e.getKey()+e.getValue()+" ")
.forEach(System.out::print);
Output: b1 e3 k2 o2 p1 r1
For printing the output based on the frequency of characters, sort the map using value Map.Entry.comparingByValue()
.
this would display the output in ascending order. since we want the most frequent once in the beginning we’ll pass comparator (a, b) -> b.compareTo(a)
Stream.of(input.toLowerCase().split(""))
.collect(Collectors.groupingBy(e -> e, Collectors.counting()))
.entrySet()
.stream()
.sorted(Map.Entry.comparingByValue((a, b) -> b.compareTo(a))))
.map(e -> e.getKey()+e.getValue()+" ")
.forEach(System.out::print);
Output: e3 k2 o2 p1 r1 b1
Taking a look at return type at each step.
The complete code with all the mentioned methods.
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class FrequencyCount {
public static void main(String[] args) {
String input = "bookkeeper";
var mapOfChar = getMapOfCharacterFrequency(input);
var linkedHashMapOfChar = getLinkedHashMapOfCharacterFrequency(input);
System.out.println("\nfreqOfCharacters");
freqOfCharacters(mapOfChar.entrySet());
System.out.println("\nfreqInOrderOfAppearance");
freqOfCharacters(linkedHashMapOfChar.entrySet());
System.out.println("\nfreqInOrderOfAlphabetic");
freqInOrderOfAlphabetic(mapOfChar.entrySet());
System.out.println("\ncharactersInOrderOfMostOccurring");
charactersInOrderOfMostOccurring(mapOfChar.entrySet());
}
private static Map<String, Long> getMapOfCharacterFrequency(String input) {
return Stream.of(input.split(""))
.collect(Collectors.groupingBy(e -> e, Collectors.counting()));
}
private static Map<String, Long> getLinkedHashMapOfCharacterFrequency(String input) {
return Stream.of(input.split(""))
.collect(Collectors.groupingBy(e -> e, LinkedHashMap::new, Collectors.counting()));
}
private static void freqOfCharacters(Set<Map.Entry<String, Long>> input) {
input.stream()
.map(e -> e.getKey()+e.getValue()+" ")
.forEach(System.out::print);
}
private static void freqInOrderOfAlphabetic(Set<Map.Entry<String, Long>> input) {
input.stream()
.sorted(Map.Entry.comparingByKey())
.map(e -> e.getKey()+e.getValue()+" ")
.forEach(System.out::print);
}
private static void charactersInOrderOfMostOccurring(Set<Map.Entry<String, Long>> input) {
input.stream()
.sorted(Map.Entry.comparingByValue((o1, o2) -> o2.compareTo(o1)))
.map(e -> e.getKey()+e.getValue()+" ")
.forEach(System.out::print);
}
}
The same code can be used for finding the frequency for words in a sentence.
Just add a space (or word separator) in the split regex input.toLowerCase().split(" ");