dtonhofer
Functional Programming in Java, Second Edition: All the code changes for Chapter 4 in two files
Again, some code suggestions for “Chapter 4”, similar to “Chapter 3”
(one would wish to have better markdown syntax highlighting to make the below more readable)
Person.java
package chapter4;
import java.util.List;
import java.util.Objects;
// ---
// The "Person" of "transforming/fpij/Person.java"
// on page 68 of "Functional Programming in Java".
// Basically no changes except a checking initializer.
// ---
public record Person(String firstName, String lastName, List<String> emailAddresses) {
public Person {
Objects.requireNonNull(firstName);
Objects.requireNonNull(lastName);
Objects.requireNonNull(emailAddresses);
}
public String fullName() {
return firstName + " " + lastName;
}
public static List<Person> getPeople() {
return List.of(
new Person("John", "Doe", List.of()),
new Person("Sara", "Walker", List.of("sara@example.com")),
new Person("Mike", "Baker",
List.of("mike@example.com", "baker@example.com")),
new Person("Dev", "Shah",
List.of("dev@example.com", "shah@example.com")),
new Person("Sara", "Lee",
List.of("slee@example.org", "lee@example.com")),
new Person("Nancy", "Xie",
List.of("nancy@example.com", "xie@example.com", "nx@example.com")),
new Person("Jill", "Smith", List.of("jill@example.com"))
);
}
}
TransformingData.java
package chapter4;
import org.junit.jupiter.api.Test;
import java.util.ArrayList;
import java.util.DoubleSummaryStatistics;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import static java.util.Comparator.comparing;
import static java.util.stream.Collectors.*;
// ---
// Various code based on Chapter 4 of "Functional Programming in Java"
// Instead of having multiple files and multiple main() we code it into a JUnit test case.
// ---
// Used in printing: A String and a Number
record PairN(String str, Number num) {
public String toString() {
return str + ": " + num + " (" + num.getClass().getSimpleName() + ")";
}
public static String listToString(List<PairN> pairs) {
return pairs.stream()
.map(PairN::toString)
.collect(Collectors.joining("\n"));
}
}
// Used in printing: A String and a Boolean
record PairB(String str, Boolean b) {
public String toString() {
return str + ": " + b;
}
public static String listToString(List<PairB> pairs) {
return pairs.stream()
.map(PairB::toString)
.collect(Collectors.joining("\n"));
}
}
public class TransformingData {
private static <T> String toStringListOfStuff(final List<T> list) {
// can even call this statically!
return list.stream().map(T::toString).collect(Collectors.joining("\n"));
}
// Originally "transforming/fpij/AverageNumberOfEmailAddresses.java" on page 69,
// which was given using its own class.
// Separated computation from the printing.
@Test
void averageNumberOfEmailAddresses() {
final var people = Person.getPeople();
final double res = people.stream()
.collect(averagingDouble(person -> person.emailAddresses().size()));
System.out.println("Average number of email addresses: " + res);
}
// Originally "transforming/fpij/Statistics.java" on page 70,
// which was given using its own class.
// Separated computation from the printing.
@Test
void statistics() {
final var people = Person.getPeople();
// https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/DoubleSummaryStatistics.html
final DoubleSummaryStatistics statistics =
people.stream()
.collect(
summarizingDouble(person -> person.emailAddresses().size()));
final var pairs = new ArrayList<PairN>();
pairs.add(new PairN("Number of people", statistics.getCount()));
pairs.add(new PairN("Number of email addresses", statistics.getSum()));
pairs.add(new PairN("Average number of email addresses", statistics.getAverage()));
pairs.add(new PairN("Max number of email addresses", statistics.getMax()));
pairs.add(new PairN("Min number of email addresses", statistics.getMin()));
System.out.println(PairN.listToString(pairs));
}
// Code on page 73
@Test
void flattenEmailAddresses() {
final var people = Person.getPeople();
final List<String> emails = people.stream()
.flatMap(person -> person.emailAddresses().stream())
.toList();
// System.out.println(emails);
System.out.println(toStringListOfStuff(emails));
}
// Code on page 74 but the criterium size() > 0 has been replaced by .isEmpty().
// Separated computation from the printing.
@Test
void checkingForCriteriaAny() {
final var people = Person.getPeople();
final var pairs = new ArrayList<PairB>();
pairs.add(new PairB(
"Anyone has email address",
people.stream().anyMatch(person -> !person.emailAddresses().isEmpty())));
pairs.add(new PairB(
"Anyone has more than 10 email address",
people.stream().anyMatch(person -> person.emailAddresses().size() > 10)));
System.out.println(PairB.listToString(pairs));
}
// Code on page 74 but the criterium size() > 0 has been replaced by .isEmpty().
// The criterium >= 0 should probably be > 1 for interestingness.
// Separated computation from the printing.
@Test
void checkingForCriteriaAll() {
final var people = Person.getPeople();
final var pairs = new ArrayList<PairB>();
// modified from the book code which uses allMatch() in all cases!
pairs.add(new PairB(
"Everyone has at least one email address (1)",
people.stream().noneMatch(person -> person.emailAddresses().isEmpty())));
pairs.add(new PairB(
"Everyone has at least one email address (2)",
people.stream().allMatch(person -> person.emailAddresses().size() > 1)));
// IDE warns: Condition 'person.emailAddresses().size() >= 0' is always 'true'
pairs.add(new PairB("Everyone has zero or more email address (always true)",
people.stream().allMatch(person -> person.emailAddresses().size() >= 0)));
System.out.println(PairB.listToString(pairs));
}
// Code on page 75
// Separated computation from the printing.
@Test
void partitioningByBoolean() {
final var people = Person.getPeople();
final Map<Boolean, List<Person>> partitions =
people.stream()
.collect(partitioningBy(person -> person.emailAddresses().size() > 1));
final var pairs = new ArrayList<PairN>();
pairs.add(new PairN("Number of people with at most one email address", partitions.get(false).size()));
pairs.add(new PairN("Number of people with multiple email addresses", partitions.get(true).size()));
System.out.println(PairN.listToString(pairs));
}
// Code on page 76 bottom, 77 top.
// Separated computation from the printing.
@Test
void countingOccurrencesWithLongResult() {
final var people = Person.getPeople();
final Map<String, Long> namesCount = people.stream()
.collect(
groupingBy(
Person::firstName,
counting()));
final List<PairN> pairs = mapToPairs(namesCount);
System.out.println(PairN.listToString(pairs));
}
// Code on page 77 middle.
// Separated computation from the printing.
@Test
void countingOccurrencesWithIntResult() {
final var people = Person.getPeople();
final Map<String, Integer> namesCount = people.stream()
.collect(
groupingBy(
Person::firstName,
collectingAndThen(counting(), Long::intValue)));
final List<PairN> pairs = mapToPairs(namesCount);
System.out.println(PairN.listToString(pairs));
}
// Helper for above
private static List<PairN> mapToPairs(final Map<String, ? extends Number> map) {
return map.entrySet().stream().map(entry -> new PairN(entry.getKey(), entry.getValue())).collect(toList());
}
// Code on page 78 top
@Test
void summingCountingNumberOfEmailAddressesByLastName() {
final var people = Person.getPeople();
final Map<String, Integer> namesAndEmailAddressesCount =
people.stream()
.collect(
groupingBy(
Person::lastName,
summingInt(person -> person.emailAddresses().size())));
final List<PairN> pairs = mapToPairs(namesAndEmailAddressesCount);
System.out.println(PairN.listToString(pairs));
}
// Code on page 79 top.
// The ".com" criterium has been moved to a name Predicate<> for legibility.
// The result's type Map<String, List<String>> is given for clarity.
// Separated computation from the printing, with dedicated formatting.
@Test
void filteringEmailAddressesEndingInDotComByLastName() {
final var people = Person.getPeople();
final Predicate<String> endsWithDotCom = (String s) -> s.endsWith(".com");
final Map<String, List<String>> lastNamesAndEmailAddressesFiltered =
people.stream()
.collect(
groupingBy(
Person::lastName,
flatMapping(
(person -> person.emailAddresses().stream()),
filtering(
endsWithDotCom,
toList()))));
var txt = lastNamesAndEmailAddressesFiltered.entrySet().stream()
.map(entry -> entry.getKey() + "=" + entry.getValue())
.collect(Collectors.joining("\n", "{\n", "\n}"));
System.out.println(txt);
// System.out.println(lastNamesAndEmailAddressesFiltered);
}
// Code on page 80 bottom.
// The common function in maxBy() and minBy() has been moved
// for legibility and suppression of repetition.
// The result's type Map<String, String> is given for clarity.
@Test
void teeingOperations() {
final var people = Person.getPeople();
final Function<Person, Integer> emailCountOfPerson = (Person person) -> person.emailAddresses().size();
final Map<String, String> leastAndMostEmailAddressPerson =
people.stream()
.collect(
teeing(
minBy(comparing(emailCountOfPerson)),
maxBy(comparing(emailCountOfPerson)),
(min, max) ->
Map.of("least", min.map(Person::fullName).orElse(""),
"most", max.map(Person::fullName).orElse(""))));
System.out.println(leastAndMostEmailAddressPerson);
}
}
Popular Pragmatic Bookshelf topics
In Chapter 3, the source for index introduces Config on page 31, followed by more code including tests; Config isn’t introduced until pag...
New
The following is cross-posted from the original Ray Tracer Challenge forum, from a post by garfieldnate. I’m cross-posting it so that the...
New
The generated iex result below should list products instead of product for the metadata. (page 67)
iex> product = %Product{}
%Pento....
New
Title: Intuitive Python: docker run… denied error (page 2)
Attempted to run the docker command in both CLI and Powershell
PS C:\Users\r...
New
I’m under the impression that when the reader gets to page 136 (“View Data with the Database Inspector”), the code SHOULD be able to buil...
New
I’m a newbie to Rails 7 and have hit an issue with the bin/Dev script mentioned on pages 112-113.
Iteration A1 - Seeing the list of prod...
New
Book: Programming Phoenix LiveView, page 142 (157/378), file lib/pento_web/live/product_live/form_component.ex, in the function below:
d...
New
Modern front-end development for Rails, second edition - Struggling to get the first chapter to work
After running /bin/setup, the first error was: The foreman' command exists in these Ruby versions: That was easy to fix: gem install fore...
New
@mfazio23
I’m following the indications of the book and arriver ad chapter 10, but the app cannot be compiled due to an error in the Bas...
New
From page 13:
On Python 3.7, you can install the libraries with pip by running these commands inside a Python venv using Visual Studio ...
New
Other popular topics
If it’s a mechanical keyboard, which switches do you have?
Would you recommend it? Why?
What will your next keyboard be?
Pics always w...
New
Write Elixir tests that you can be proud of. Dive into Elixir’s test philosophy and gain mastery over the terminology and concepts that u...
New
Which, if any, games do you play? On what platform?
I just bought (and completed) Minecraft Dungeons for my Nintendo Switch. Other than ...
New
I’m thinking of buying a monitor that I can rotate to use as a vertical monitor?
Also, I want to know if someone is using it for program...
New
Design and develop sophisticated 2D games that are as much fun to make as they are to play. From particle effects and pathfinding to soci...
New
I’ve been hearing quite a lot of comments relating to the sound of a keyboard, with one of the most desirable of these called ‘thock’, he...
New
Just done a fresh install of macOS Big Sur and on installing Erlang I am getting:
asdf install erlang 23.1.2
Configure failed.
checking ...
New
Build highly interactive applications without ever leaving Elixir, the way the experts do. Let LiveView take care of performance, scalabi...
New
Explore the power of Ash Framework by modeling and building the domain for a real-world web application.
Rebecca Le @sevenseacat and ...
New
Ok, well here are some thoughts and opinions on some of the ergonomic keyboards I have, I guess like mini review of each that I use enoug...
New
Categories:
Sub Categories:
Popular Portals
- /elixir
- /rust
- /wasm
- /ruby
- /erlang
- /phoenix
- /keyboards
- /python
- /js
- /rails
- /security
- /go
- /swift
- /vim
- /clojure
- /java
- /emacs
- /haskell
- /svelte
- /onivim
- /typescript
- /kotlin
- /c-plus-plus
- /crystal
- /tailwind
- /react
- /gleam
- /ocaml
- /flutter
- /elm
- /vscode
- /ash
- /html
- /opensuse
- /zig
- /centos
- /deepseek
- /php
- /scala
- /react-native
- /lisp
- /sublime-text
- /textmate
- /nixos
- /debian
- /agda
- /django
- /deno
- /kubuntu
- /arch-linux
- /nodejs
- /spring
- /ubuntu
- /revery
- /manjaro
- /julia
- /lua
- /diversity
- /markdown
- /slackware









