1

I am trying to read from a file, count the frequency of each letter, and print the results. Uppercase letters should be treated as their lowercase equivalent. I have example code to start with which counts the frequency of different words in a file:

Pattern pattern = Pattern.compile("\\s+");
Map<String, Long> wordCounts =
        Files.lines(Paths.get("myFile.txt"))
                .flatMap(pattern::splitAsStream)
                .collect(Collectors.groupingBy(String::toLowerCase, TreeMap::new, Collectors.counting()));

I am to modify this code to count letters instead. I believe most of my difficulty is in understanding what the object becomes as the code progresses and what I can do with the result. I know that Files.lines() outputs a Stream String where each String is actually one line in the file. From this answer, I know I could convert a String to a Stream Character with this code:

Stream<Character> sch = "abc".chars().mapToObj(i -> (char)i);

However, I can't quite figure out a way to use it to convert a Stream String.

Stefan Zobel
  • 3,182
  • 7
  • 28
  • 38
Jack J
  • 1,514
  • 3
  • 20
  • 28

1 Answers1

4

With flatMap() you can map a stream element into a new stream.

In the original code a line is mapped into words with .flatMap(pattern::splitAsStream) , but you can just as easily map the line to characters with .flatMap(s -> s.chars().mapToObj(i -> (char)i)).

Kayaman
  • 72,141
  • 5
  • 83
  • 121
  • 1
    To expand a bit: You just need to know that `chars` returns you an `IntStream` instead of a `CharStream` as that does not exist (read here for reasons: https://stackoverflow.com/questions/22435833/why-is-string-chars-a-stream-of-ints-in-java-8). This is why you still need to use `mapToObj`. – Ben Mar 16 '18 at 07:38
  • 1
    Indeed. Although nothing stops you from keeping them as `ints` when counting them, and only when you display the results you cast them back to `char`. Not that it makes any significant difference, and it's easier to see what's going on when you map the ints to `Character`s. – Kayaman Mar 16 '18 at 07:40
  • 1
    That's totally right. If you can work on an integer value there is no reason to convert it in the first place. Just wanted to make that note as many people would expect a `CharStream` when using something called `chars` – Ben Mar 16 '18 at 07:42
  • String::chars in .flatMap(String::chars) gives me the error "Bad return type. Cannot convert IntStream to Stream extends R>". – Jack J Mar 16 '18 at 08:10
  • Oops, some parenthesis paranoia. Fixed, couldn't get away with a simple method reference. – Kayaman Mar 16 '18 at 08:12
  • What would be the best way to filter out whitespace and punctuation? – Jack J Mar 16 '18 at 16:33
  • @JackJ a simple way would be to just add a `.filter(c -> ...)` there and use either a regex or just something like `Character.isLetterOrDigit(c)` depending on your needs. – Kayaman Mar 19 '18 at 17:20