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

}

Popular Pragmatic topics Top

Razor54672
The answer to 3rd Problem of Chapter 5 (Making Choices) of “Practical Programming, Third Edition” seems incorrect in the given answer ke...
New
ianwillie
Hello Brian, I have some problems with running the code in your book. I like the style of the book very much and I have learnt a lot as...
New
yulkin
your book suggests to use Image.toByteData() to convert image to bytes, however I get the following error: "the getter ‘toByteData’ isn’t...
New
sdmoralesma
Title: Web Development with Clojure, Third Edition - migrations/create not working: p159 When I execute the command: user=&gt; (create-...
New
jdufour
Hello! On page xix of the preface, it says there is a community forum "… for help if your’re stuck on one of the exercises in this book… ...
New
New
brian-m-ops
#book-python-testing-with-pytest-second-edition Hi. Thanks for writing the book. I am just learning so this might just of been an issue ...
New
jskubick
I found an issue in Chapter 7 regarding android:backgroundTint vs app:backgroundTint. How to replicate: load chapter-7 from zipfile i...
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
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

Exadra37
I am thinking in building or buy a desktop computer for programing, both professionally and on my free time, and my choice of OS is Linux...
New
siddhant3030
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
AstonJ
Curious to know which languages and frameworks you’re all thinking about learning next :upside_down_face: Perhaps if there’s enough peop...
New
AstonJ
poll poll Be sure to check out @Dusty’s article posted here: An Introduction to Alternative Keyboard Layouts It’s one of the best write-...
New
AstonJ
I have seen the keycaps I want - they are due for a group-buy this week but won’t be delivered until October next year!!! :rofl: The Ser...
New
AstonJ
Do the test and post your score :nerd_face: :keyboard: If possible, please add info such as the keyboard you’re using, the layout (Qw...
New
Exadra37
I am asking for any distro that only has the bare-bones to be able to get a shell in the server and then just install the packages as we ...
New
mafinar
Crystal recently reached version 1. I had been following it for awhile but never got to really learn it. Most languages I picked up out o...
New
husaindevelop
Inside our android webview app, we are trying to paste the copied content from another app eg (notes) using navigator.clipboard.readtext ...
New
CommunityNews
A Brief Review of the Minisforum V3 AMD Tablet. Update: I have created an awesome-minisforum-v3 GitHub repository to list information fo...
New

Latest in PragProg

View all threads ❯