CMSI 386
Homework #3
Due: 2018-11-06

For this homework assignment, you'll revisit a language you probably know a little bit of already—Java. This is intended to be a short homework assignment, so work in groups of three or four.

Readings

Please read:

Study those three tutorials.

Instructions

You will be adding to the private GitHub repository you started in the previous assignment. After adding and modifying files, your repository will look like this:

  .
  ├── README.md
  ├── .gitignore
  ├── homework1/
  │   └── (existing homework 1 files)
  ├── homework2/
  │   └── (existing homework 2 files)
  └── homework3
      ├── word_count_input.txt
      ├── batting_average_input.txt
      ├── StreamPracticeTest.java
      └── StreamPractice.java

Please submit only one solution set for your team. It does not matter under whose GitHub account your work is stored. What matters, if you wish to not receive a zero, is that I can clone your repo and run my tests. Since the repo is to be private, please allow me as a contributor to your repo (so I can run and comment on your work). My github name is rtoal.

Make sure you are using a linter. Non-linted code will be severely marked down to the point where your grade is not a passing one.

Setting up your Project

Although Java projects are normally constructed with Maven and Gradle and tested with JUnit, this assignment is really about you brushing up on modern features in a language you already know, so we’ll go rogue and just test with some assert statements. For StreamPracticeTest.java use the code below. After you write StreamPractice.java, you can run your tests with:

javac StreamPracticeTest.java && java -ea StreamPracticeTest
Here’s the test script:
import java.io.BufferedReader;
import java.io.FileReader;
import java.util.Map;
import java.util.TreeMap;

public class StreamPracticeTest {

    private static void testWordCount() throws Exception {
        var reader = new BufferedReader(new FileReader("word_count_input.txt"));
        var actual = StreamPractice.wordCountByLength(reader);
        var expected = Map.of(1, 2L, 2, 8L, 3, 7L, 4, 5L, 6, 3L, 7, 1L, 9, 1L, 13, 1L);
        assert actual.equals(expected);
    }

    private static void testBattingAverage() throws Exception {
        var reader = new BufferedReader(new FileReader("batting_average_input.txt"));
        var actual = StreamPractice.bestBatterByTeam(reader);
        assert actual.size() == 3;
        StreamPractice.Batter soxBatter = actual.get("Sox").orElse(null);
        assert soxBatter.name.equals("Bhekisisa") && soxBatter.average == 0.25;
        StreamPractice.Batter lionsBatter = actual.get("Lions").orElse(null);
        assert lionsBatter.name.equals("Ciara") && lionsBatter.average == 0.903125;
        StreamPractice.Batter tigersBatter = actual.get("Tigers").orElse(null);
        assert tigersBatter.name.equals("Eimi") && tigersBatter.average == 0.29;
    }

    public static void main(String[] args) throws Exception {
        testWordCount();
        testBattingAverage();
    }
}

It refers to two test files. The word count input file should look like this:

The area of a circle is πr²
and the circumference of a CirclE is 2πr
See you mañana by THE ❉café
Pōmaikaʻi
Ruby was created by まつもと ゆきひろ

And the batting average input file should be:

Ani,Sox,30,9
Bhekisisa,Sox,100,25
Ciara,Lions,320,289
Dora,Tigers,70,60
Eimi,Tigers,100,29
Francesca,Sox,300,50
Galina,Lions,120,66
Henrietta,Lions,80,70

Your file, StreamPractice.java should be structured like this:

// IMPORTS GO HERE

public class StreamPractice {

    static Pattern nonWord = Pattern.compile("[^\\p{L}']+");

    public static Map<Integer, Long> wordCountByLength(BufferedReader reader) {
        // Return a tree map whose keys are word lengths
        // and corresponding values are the number of words
        // in the reader with that length.
    }

    public static class Batter {
        String name;
        String team;
        int atBats;
        int hits;
        double average;
        Batter(String line) {
            String[] components = line.split("\\s*,\\s*");
            this.name = components[0];
            this.team = components[1];
            this.atBats = Integer.parseInt(components[2]);
            this.hits = Integer.parseInt(components[3]);
            this.average = (double)this.hits / (double)this.atBats;
        }
    }

    public static Map<String, Optional<Batter>> bestBatterByTeam(BufferedReader reader) {
        // Return a map that records, for each team, the batter with
        // the highest average over all batters that have at least
        // 100 at-bats.
    }
}