dtonhofer

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);
    }

}

Where Next?

Popular Pragmatic Bookshelf topics Top

abtin
page 20: … protoc command… I had to additionally run the following go get commands in order to be able to compile protobuf code using go...
New
iPaul
page 37 ANTLRInputStream input = new ANTLRInputStream(is); as of ANTLR 4 .8 should be: CharStream stream = CharStreams.fromStream(i...
New
johnp
Hi Brian, Looks like the api for tinydb has changed a little. Noticed while working on chapter 7 that the .purge() call to the db throws...
New
jesse050717
Title: Web Development with Clojure, Third Edition, pg 116 Hi - I just started chapter 5 and I am stuck on page 116 while trying to star...
New
Chrichton
Dear Sophie. I tried to do the “Authorization” exercise and have two questions: When trying to plug in an email-service, I found the ...
New
adamwoolhether
When trying to generate the protobuf .go file, I receive this error: Unknown flag: --go_opt libprotoc 3.12.3 MacOS 11.3.1 Googling ...
New
brunogirin
When trying to run tox in parallel as explained on page 151, I got the following error: tox: error: argument -p/–parallel: expected one...
New
brunogirin
When running tox for the first time, I got the following error: ERROR: InterpreterNotFound: python3.10 I realised that I was running ...
New
taguniversalmachine
It seems the second code snippet is missing the code to set the current_user: current_user: Accounts.get_user_by_session_token(session["...
New
gorkaio
root_layout: {PentoWeb.LayoutView, :root}, This results in the following following error: no “root” html template defined for PentoWeb...
New

Other popular topics Top

AstonJ
A thread that every forum needs! Simply post a link to a track on YouTube (or SoundCloud or Vimeo amongst others!) on a separate line an...
New
ohm
Which, if any, games do you play? On what platform? I just bought (and completed) Minecraft Dungeons for my Nintendo Switch. Other than ...
New
AstonJ
We have a thread about the keyboards we have, but what about nice keyboards we come across that we want? If you have seen any that look n...
New
dimitarvp
Small essay with thoughts on macOS vs. Linux: I know @Exadra37 is just waiting around the corner to scream at me “I TOLD YOU SO!!!” but I...
New
PragmaticBookshelf
Tailwind CSS is an exciting new CSS framework that allows you to design your site by composing simple utility classes to create complex e...
New
First poster: joeb
The File System Access API with Origin Private File System. WebKit supports new API that makes it possible for web apps to create, open,...
New
PragmaticBookshelf
Author Spotlight: VM Brasseur @vmbrasseur We have a treat for you today! We turn the spotlight onto Open Source as we sit down with V...
New
PragmaticBookshelf
Author Spotlight: Peter Ullrich @PJUllrich Data is at the core of every business, but it is useless if nobody can access and analyze ...
New
AstonJ
This is a very quick guide, you just need to: Download LM Studio: https://lmstudio.ai/ Click on search Type DeepSeek, then select the o...
New
Fl4m3Ph03n1x
Background Lately I am in a quest to find a good quality TTS ai generation tool to run locally in order to create audio for some videos I...
New

Latest in Functional Programming in Java, Second Edition

Functional Programming in Java, Second Edition Portal

Sub Categories: