Eclipse Collections Kata に挑む

# 概要
Java の高機能 Collection Framework である [Eclipse Collections](https://www.eclipse.org/collections/ja/) には [Kata](https://github.com/eclipse/eclipse-collections-kata) というトレーニングマテリアルが用意されています。Kata という名前は武術の「型」が元だそうです。

Eclipse Collections がどういうものかについては [@opengl-8080](http://qiita.com/opengl-8080) さんの [GS Collections 使い方メモ](http://qiita.com/opengl-8080/items/a13773e0653800e23323) が詳しいです。なお、GS Collections は Eclipse Collections の旧称です。

## Eclipse Collections Kata とはどんなものか？
未完成の UnitTest(JUnit 形式) のコードを修正していくことで、Eclipse Collections の機能や使い方を学習できるというトレーニングマテリアルです。[GitHub で公開されており](https://github.com/eclipse/eclipse-collections-kata)、いつでもダウンロードして始めることができます。学習後に、ケースごとの使い方を具体的な例で振り返る資料としても使えそうです。

# 導入
下記の [@deaf_tadashi](http://qiita.com/deaf_tadashi) さんの記事が参考になります。
[Eclipse Collections Kata をやるにあたって知っておくとよさげなこと](http://qiita.com/deaf_tadashi/items/2279dec0cc0d132c9585)

# 構成
2016/05/28(Sat) 時点では pet-kata と company-kata の2種類が用意されています。pet-kata の方が問題数が少なくシンプルな問題が多いです。

## pet-kata
| Class | Tests
|:---|---:
| Exercise1Test | 4
| Exercise2Test | 11
| Exercise3Test | 3
| Exercise4Test | 4
| Exercise5Test | 5

## company-kata
| Class | Tests
|:---|---:
| Exercise1Test | 3
| Exercise2Test | 9
| Exercise3Test | 3
| Exercise4Test | 5
| Exercise5Test | 2
| Exercise6Test | 5
| Exercise7Test | 3
| Exercise8Test | 6

# 解答
実際に不完全な UnitTest のコードを修正し、テストにパスするようにしていきます。修正したテストコードは Gradle で実行してもよいですし、 IDE で1つずつ実行してもよいでしょう。IDE だと進捗をグラフィカルに把握できるのが利点です。

[私の解答例](https://github.com/toastkidjp/eclipse-collections-kata/tree/toastkidjp_all_solved)

# 感想
早くやっておくべきだったと思います。 company-kata の Exercise8Test を除くと、結構やさしめの問題でした。ライブラリを普及させるためにはこうした教育マテリアルがあった方が、使いたい側にとっては非常に有益に思います。

単純なEclipse Collectionsの学習にとどまらず、今まで使えていなかった flat○○ 操作やメソッド参照の使い方を理解できたのは大きいです。 Eclipse Collections では特に Bag の強力さがよくわかります。ArrayIterate や Multimap も設計がよく考えられた使いやすいクラスです。入門用の学習教材であるため、すべてのクラスについての問題が用意されているわけではありませんが、これをやっておくだけで使い方に幅が出るはずです。

本質とはまったく関係ありませんが、メソッド名のあとの中括弧を改行するスタイルだとメソッド名のコピペが楽だと気付きました。

---

以下、実際に解いてみた際のメモです。まだ解いていない方はご覧にならないことをお勧めします。

---

# [pet-kata](https://github.com/toastkidjp/eclipse-collections-kata/tree/toastkidjp_all_solved/pet-kata/src/test/java/org/eclipse/collections/petkata)

## [Exercise1](https://github.com/toastkidjp/eclipse-collections-kata/blob/toastkidjp_all_solved/pet-kata/src/test/java/org/eclipse/collections/petkata/Exercise1Test.java)

### 前提
下記3つのフィールドを持つ Person クラスがあります。

```java:Person
private final String firstName;
private final String lastName;
private final MutableList<Pet> pets = FastList.newList();
```

そして、Person クラスの List である people が最初に与えられています。

### getFirstNamesOfAllPeople
まあ、読んで字のごとしです。

```java:getFirstNamesOfAllPeople
@Test
public void getFirstNamesOfAllPeople() {
    final MutableList<Person> people = this.people;
    final MutableList<String> firstNames = people.collect(person -> person.getFirstName());
    final MutableList<String> expectedFirstNames
         = Lists.mutable.with("Mary", "Bob", "Ted", "Jake", "Barry", "Terry", "Harry", "John");
    Assert.assertEquals(expectedFirstNames, firstNames);
}
```

### getNamesOfMarySmithsPets
上のとそんなに変わりません。

```java:getNamesOfMarySmithsPets
@Test
public void getNamesOfMarySmithsPets() {
    final Person person = this.getPersonNamed("Mary Smith");
    final MutableList<Pet> pets = person.getPets();
    //Replace null, with a transformation method on MutableList.
    final MutableList<String> names = person.getPets().collect(pet -> pet.getName()); 
    Assert.assertEquals("Tabby", names.makeString());
}
```

### getPeopleWithCats & getPeopleWithoutCats
メソッドを1つ変えるだけで解けるのでまとめます。

select が Collection に残す要素を**選ぶ**メソッドで、reject が条件に一致する要素を Collection から**除外**するメソッドです。条件式に**否定形を書かなくてよい**点が素晴らしいです。

```java:getPeopleWithCats
    @Test
    public void getPeopleWithCats() {
        final MutableList<Person> people = this.people;
        final MutableList<Person> peopleWithCats
             = people.select(person -> person.hasPet(PetType.CAT));
        Verify.assertSize(2, peopleWithCats);
    }
```

```java:getPeopleWithoutCats
    @Test
    public void getPeopleWithoutCats() {
        final MutableList<Person> people = this.people;
        final MutableList<Person> peopleWithoutCats
             = people.reject(person -> person.hasPet(PetType.CAT));
        Verify.assertSize(6, peopleWithoutCats);
    }
```

## [Exercise2Test](https://github.com/toastkidjp/eclipse-collections-kata/blob/toastkidjp_all_solved/pet-kata/src/test/java/org/eclipse/collections/petkata/Exercise2Test.java)

### doAnyPeopleHaveCats
いつも Lambda で直接記述している Predicate を外で定義します。

```java:doAnyPeopleHaveCats
    @Test
    public void doAnyPeopleHaveCats() {
        //replace null with a predicate which checks for PetType.CAT
        final Predicate<Person> predicate = person -> person.hasPet(PetType.CAT);
        Assert.assertTrue(this.people.anySatisfy(predicate));
    }
```

### doAllPeopleHavePets
同上

```java:doAllPeopleHavePets
    @Test
    public void doAllPeopleHavePets() {
        final Predicate<Person> predicate = person -> person.isPetPerson();
        //replace with something that checks if all people have pets
        final boolean result = people.allSatisfy(predicate);
        Assert.assertFalse(result);
    }
```

### howManyPeopleHaveCats
条件に一致する要素数を数える count メソッドを使います。

```java:howManyPeopleHaveCats
    @Test
    public void howManyPeopleHaveCats() {
        final int count = people.count(person -> person.hasPet(PetType.CAT));
        Assert.assertEquals(2, count);
    }
```

### findMarySmith
条件に一致する**最初の**要素を取り出すには detect メソッドを使います。IDE で最初にサジェストされます。

```java:findMarySmith
    @Test
    public void findMarySmith() {
        final Person result = people.detect(
                person -> "Mary".equals(person.getFirstName()) && "Smith".equals(person.getLastName()));
        Assert.assertEquals("Mary", result.getFirstName());
        Assert.assertEquals("Smith", result.getLastName());
    }
```


### getPeopleWithPets
Pet を所持していない Person を reject することで、 Pet を所持している Person の List を作ります。

```java:getPeopleWithPets
    @Test
    public void getPeopleWithPets() {
        final MutableList<Person> petPeople = this.people.reject(person -> person.getPets().isEmpty()); //replace with only the pet owners
        Verify.assertSize(7, petPeople);
    }
```

なお Eclipse Collections の eclipse-collections-testutils では、 Collection のテストで便利な独自 assertion を実装しています。

```
Verify.assertSize(7, petPeople);
```

### getAllPetsOfAllPeople
これ、本当に合っているのでしょうか……突然 flatCollect が出てきます。

```java:getAllPetsOfAllPeople
    @Test
    public void getAllPetsOfAllPeople() {
        final Function<Person, Iterable<PetType>> function = person -> person.getPetTypes();
        // ???
        final MutableSet<PetType> petTypes = this.people.flatCollect(function).toSet();
        Assert.assertEquals(
                Sets.mutable.with(PetType.CAT, PetType.DOG, PetType.TURTLE, PetType.HAMSTER, PetType.BIRD, PetType.SNAKE),
                petTypes);
    }
```

### getFirstNamesOfAllPeople
collect で変形させるだけのようです。

```java:getFirstNamesOfAllPeople
    @Test
    public void getFirstNamesOfAllPeople() {
        final MutableList<String> firstNames = people.collect(person -> person.getFirstName());
        Assert.assertEquals(
                Lists.mutable.with("Mary", "Bob", "Ted", "Jake", "Barry", "Terry", "Harry", "John"),
                firstNames);
    }
```

### doAnyPeopleHaveCatsRefactor
Eclipse Collections ではメソッド参照を使いやすくするために、それ用のメソッドが xxWith という名前で用意されています。

```java:doAnyPeopleHaveCatsRefactor
    @Test
    public void doAnyPeopleHaveCatsRefactor() {
        final boolean peopleHaveCatsLambda = this.people.anySatisfy(person -> person.hasPet(PetType.CAT));
        Assert.assertTrue(peopleHaveCatsLambda);

        //use method reference, NOT lambdas, to solve the problem below
        final boolean peopleHaveCatsMethodRef = this.people.anySatisfyWith(Person::hasPet, PetType.CAT);
        Assert.assertTrue(peopleHaveCatsMethodRef);
    }
```

### doAllPeopleHaveCatsRefactor
上に同じです。

```java:doAllPeopleHaveCatsRefactor
    @Test
    public void doAllPeopleHaveCatsRefactor() {
        final boolean peopleHaveCatsLambda = this.people.allSatisfy(person -> person.hasPet(PetType.CAT));
        Assert.assertFalse(peopleHaveCatsLambda);

        //use method reference, NOT lambdas, to solve the problem below
        final boolean peopleHaveCatsMethodRef = this.people.allSatisfyWith(Person::hasPet, PetType.CAT);
        Assert.assertFalse(peopleHaveCatsMethodRef);
    }
```

### getPeopleWithCatsRefator
selectWith を使います。

```java:getPeopleWithCatsRefator
    @Test
    public void getPeopleWithCatsRefator() {
        //use method reference, NOT lambdas, to solve the problem below
        final MutableList<Person> peopleWithCatsMethodRef
            = this.people.selectWith(Person::hasPet, PetType.CAT);
        Verify.assertSize(2, peopleWithCatsMethodRef);
    }
```

### getPeopleWithoutCatsRefactor
rejectWith を使います。

```java:getPeopleWithoutCatsRefactor
    @Test
    public void getPeopleWithoutCatsRefactor() {
        //use method reference, NOT lambdas, to solve the problem below
        final MutableList<Person> peopleWithoutCatsMethodRef
            = this.people.rejectWith(Person::hasPet, PetType.CAT);
        Verify.assertSize(6, peopleWithoutCatsMethodRef);
    }
```

## [Exercise3Test](https://github.com/toastkidjp/eclipse-collections-kata/blob/toastkidjp_all_solved/pet-kata/src/test/java/org/eclipse/collections/petkata/Exercise3Test.java)
メソッド全体を引用するのがいい加減つらくなってきたので、要点だけ書いていきます。

### getCountsByPetType
要素ごとの重複数を数え上げる処理を下記のように書いていたとします。

なお、元のコードで PetType 一覧を作るのに flatCollect を使っているのに、数え上げで拡張 for 文を使っている点は特に気にしないでよいです。

```java:Before
final MutableList<PetType> petTypes = this.people.flatCollect(Person::getPets).collect(Pet::getType);
// Try to replace MutableMap<PetType, Integer> with a Bag<PetType>
final MutableMap<PetType, Integer> petTypeCounts = Maps.mutable.empty();
for (final PetType petType : petTypes) {
    Integer count = petTypeCounts.get(petType);
    if (count == null) {
        count = 0;
    }
    petTypeCounts.put(petType, count + 1);
}
```

この長ったらしい処理が Bag を使うとたった2行で書けます。

```java:After
final MutableBag<PetType> counts = Bags.mutable.empty();
petTypes.each(petType -> counts.add(petType));
```

便利すぎて涙が出ますね。

### getPeopleByLastName

```java:Before
final MutableMap<String, MutableList<Person>> lastNamesToPeople = Maps.mutable.empty();
        for (final Person person : this.people)
        {
            final String lastName = person.getLastName();
            MutableList<Person> peopleWithLastName = lastNamesToPeople.get(lastName);
            if (peopleWithLastName == null)
            {
                peopleWithLastName = Lists.mutable.empty();
                lastNamesToPeople.put(lastName, peopleWithLastName);
            }
            peopleWithLastName.add(person);
        }
```

この長ったらしい処理が Multimap (スペリングに注意、map は小文字)を使うとたった2行で書けます。

```java:After
final MutableMultimap<String, Person> byLastNameMultimap = Multimaps.mutable.list.empty();
this.people.each(person -> byLastNameMultimap.put(person.getLastName(), person));
```

Multimap の factory は、まず mutable/immutable を選び、さらに list/set/sortedSet/bag を選択できます。

Eclipse Collections の Map は Pair(key-value 1組だけの Collection) との連携が意識されており、同じ処理を下記の通り書けます。

```java:add
this.people.each(person -> byLastNameMultimap.add(Tuples.pair(person.getLastName(), person)));
```

### getPeopleByTheirPets
set の MutableMultimap を使って改善する、というお題です。

```java:Before
final MutableMap<PetType, MutableSet<Person>> peopleByPetType = Maps.mutable.empty();

        for (final Person person : this.people)
        {
            final MutableList<Pet> pets = person.getPets();
            for (final Pet pet : pets)
            {
                final PetType petType = pet.getType();
                MutableSet<Person> peopleWithPetType = peopleByPetType.get(petType);
                if (peopleWithPetType == null)
                {
                    peopleWithPetType = Sets.mutable.empty();
                    peopleByPetType.put(petType, peopleWithPetType);
                }
                peopleWithPetType.add(person);
            }
        }
```

ひとまずこのように書けます。

```java:After
final MutableMultimap<PetType, Person> peopleByPetType = Multimaps.mutable.set.empty();
for (final Person person : this.people) {
    final MutableList<Pet> pets = person.getPets();
    for (final Pet pet : pets) {
        peopleByPetType.put(pet.getType(), person);
    }
}
```

Lambda in Lambda になってしまって具合は悪いですが、こんな風に書けなくもないです。

```java:Lambda_in_Lambda
this.people.each(person -> {
    person.getPets().each(pet -> peopleByPetType.put(pet.getType(), person));
});
```

## [Exercise4Test](https://github.com/toastkidjp/eclipse-collections-kata/blob/toastkidjp_all_solved/pet-kata/src/test/java/org/eclipse/collections/petkata/Exercise4Test.java)

### Collection の修正
Eclipse Collections には [primitive の要素を保持できる Collection が用意されています](https://www.eclipse.org/collections/ja/#primitives)。このテストでは IntList と IntSet を使います。

```java:Before
MutableList<Integer> petAges = this.people
                .stream()
                .flatMap(person -> person.getPets().stream())
                .map(pet -> pet.getAge())
                .collect(Collectors.toCollection(FastList::new));
```

collectInt で IntList に変換できます。

```java:After
final MutableIntList petAges = this.people
                .flatCollect(person -> person.getPets())
                .collectInt(pet -> pet.getAge());
```

```java:Before
Set<Integer> uniqueAges = petAges.toSet();
```

toSet で変換できます。

```java:After
IntSet uniqueAges = petAges.toSet();
```


```java:Before
IntSummaryStatistics stats = petAges.stream().mapToInt(i -> i).summaryStatistics();
```

空の IntSummaryStatistics インスタンスを生成し、 petAges の要素を全部入れます。

```java:After
final IntSummaryStatistics stats = new IntSummaryStatistics();
petAges.forEach(i -> {stats.accept(i);});
```

#### IntSets の使用
もちろん primitive collection 用の factory も用意されています。

```java:Before
Assert.assertEquals(Sets.mutable.with(1, 2, 3, 4), uniqueAges);
```

```java:After
Assert.assertEquals(IntSets.mutable.with(1, 2, 3, 4), uniqueAges);
```

#### 便利な要素検査

```java:Before
Assert.assertEquals(stats.getMin(), petAges.stream().mapToInt(i -> i).min().getAsInt());
Assert.assertEquals(stats.getMax(), petAges.stream().mapToInt(i -> i).max().getAsInt());
Assert.assertEquals(stats.getSum(), petAges.stream().mapToInt(i -> i).sum());
Assert.assertEquals(stats.getAverage(), petAges.stream().mapToInt(i -> i).average().getAsDouble(), 0.0);
```

これらを計算するメソッドが全部用意されています。

```java:After
Assert.assertEquals(stats.getMin(), uniqueAges.min());
Assert.assertEquals(stats.getMax(), uniqueAges.max());
Assert.assertEquals(stats.getSum(), petAges.sum());
Assert.assertEquals(stats.getAverage(), petAges.average(), 0.0);
```

#### Match = Satisfy
```java:Before
Assert.assertTrue(petAges.stream().allMatch(i -> i > 0));
Assert.assertFalse(petAges.stream().anyMatch(i -> i == 0));
Assert.assertTrue(petAges.stream().noneMatch(i -> i < 0));
```

```java:After
Assert.assertTrue(petAges.allSatisfy(i -> i > 0));
Assert.assertFalse(petAges.anySatisfy(i -> i == 0));
Assert.assertTrue(petAges.noneSatisfy(i -> i < 0));
```

### streamsToECRefactor1
Eclipse Collections を使えば余計なメソッドが少なくなるという例です。

```java:Before
//find Bob Smith
final Person person =
                this.people.stream()
                        .filter(each -> each.named("Bob Smith"))
                        .findFirst().get();

//get Bob Smith's pets' names
final String names =
                person.getPets().stream()
                        .map(Pet::getName)
                        .collect(Collectors.joining(" & "));
```

```java:After
//find Bob Smith
final Person person =
                this.people
                        .select(each -> each.named("Bob Smith"))
                        .getFirst();

//get Bob Smith's pets' names
final String names =
                person.getPets()
                        .collect(Pet::getName)
                        .makeString(" & ");
```

### streamsToECRefactor2
```java:Before
final Map<PetType, Long> countsStream =
                Collections.unmodifiableMap(
                        this.people.stream()
                                .flatMap(person -> person.getPets().stream())
                                .collect(Collectors.groupingBy(Pet::getType,
                                        Collectors.counting())));
```

```java:After
final Bag<PetType> countsStream =
                this.people
                        .flatCollect(person -> person.getPets())
                        .collect(pet -> pet.getType())
                        .toBag();
```

なお、前の問題にもあったように Bag からの数値取り出しには occurrencesOf メソッドを使います。 Assertion の書き換えも必要でした。

### streamsToECRefactor3
引き続き Bag の力を思い知りましょう。

```java:Before
final List<Map.Entry<PetType, Long>> favoritesStream =
                this.people.stream()
                        .flatMap(p -> p.getPets().stream())
                        .collect(Collectors.groupingBy(Pet::getType, Collectors.counting()))
                        .entrySet()
                        .stream()
                        .sorted(Comparator.comparingLong(e -> -e.getValue()))
                        .limit(3)
                        .collect(Collectors.toList());
```

こうしたややこしい処理が

```java:After
        final List<ObjectIntPair<PetType>> favoritesStream =
                this.people
                        .flatCollect(p -> p.getPets())
                        .collect(pet -> pet.getType())
                        .toBag()
                        .topOccurrences(3);
```

Eclipse Collections を使うとまったく必要なく、toBag() と topOccurrences(3) で解決します。

#### Assertion の修正

```java:Before
Verify.assertContains(new AbstractMap.SimpleEntry<>(PetType.CAT, Long.valueOf(2)), favoritesStream);
Verify.assertContains(new AbstractMap.SimpleEntry<>(PetType.DOG, Long.valueOf(2)), favoritesStream);
Verify.assertContains(new AbstractMap.SimpleEntry<>(PetType.HAMSTER, Long.valueOf(2)), favoritesStream);
```

PrimitiveTuples で書き直します。

```java:After
Verify.assertContains(PrimitiveTuples.pair(PetType.CAT, 2), favoritesStream);
Verify.assertContains(PrimitiveTuples.pair(PetType.DOG, 2), favoritesStream);
Verify.assertContains(PrimitiveTuples.pair(PetType.HAMSTER, 2), favoritesStream);
```

## [Exercise5Test](https://github.com/toastkidjp/eclipse-collections-kata/blob/toastkidjp_all_solved/pet-kata/src/test/java/org/eclipse/collections/petkata/Exercise5Test.java)

### partitionPetAndNonPetPeople
partition を使うと selected と rejected 2つの Collection が生成され、それぞれ参照できます。

```java:partition
PartitionMutableList<Person> partitionMutableList
            = people.partition(person -> 0 < person.getPets().size());
```

### getOldestPet
flatCollect で Pet の一覧を作り、属性を指定して最大値を持つ Pet を取り出します。

```java:maxBy
people.flatCollect(Person::getPets).maxBy(pet -> pet.getAge());
```

### getAveragePetAge
flatCollect で Pet の一覧を作り、さらに年齢の List に変換して平均年齢を出します。

```java:average
people.flatCollect(Person::getPets)
                .collectInt(pet -> pet.getAge())
                .average();
```

### addPetAgesToExistingSet
すでにある Collection に要素を追加します。 Set で持っている addAll を使えばよいでしょう。

```java:add
final MutableIntSet petAges = IntSets.mutable.with(5);
petAges.addAll(people.flatCollect(Person::getPets).collectInt(Pet::getAge));
```

### refactorToEclipseCollections
従来の Collection を Eclipse Collections で置き換えます。

### List
```java:Before
final List<Person> people = new ArrayList<>();
people.add(new Person("Mary", "Smith").addPet(PetType.CAT, "Tabby", 2));
people.add(new Person("Bob", "Smith")
    .addPet(PetType.CAT, "Dolly", 3).addPet(PetType.DOG, "Spot", 2));
people.add(new Person("Ted", "Smith").addPet(PetType.DOG, "Spike", 4));
people.add(new Person("Jake", "Snake").addPet(PetType.SNAKE, "Serpy", 1));
people.add(new Person("Barry", "Bird").addPet(PetType.BIRD, "Tweety", 2));
people.add(new Person("Terry", "Turtle").addPet(PetType.TURTLE, "Speedy", 1));
people.add(new Person("Harry", "Hamster")
    .addPet(PetType.HAMSTER, "Fuzzy", 1).addPet(PetType.HAMSTER, "Wuzzy", 1));
people.add(new Person("John", "Doe"));
```

Collection を of メソッドで生成します。

```java:After
final MutableList<Person> people = Lists.mutable.of(
    new Person("Mary", "Smith").addPet(PetType.CAT, "Tabby", 2),
    new Person("Bob", "Smith")
        .addPet(PetType.CAT, "Dolly", 3).addPet(PetType.DOG, "Spot", 2),
    new Person("Ted", "Smith").addPet(PetType.DOG, "Spike", 4),
    new Person("Jake", "Snake").addPet(PetType.SNAKE, "Serpy", 1),
    new Person("Barry", "Bird").addPet(PetType.BIRD, "Tweety", 2),
    new Person("Terry", "Turtle").addPet(PetType.TURTLE, "Speedy", 1),
    new Person("Harry", "Hamster")
        .addPet(PetType.HAMSTER, "Fuzzy", 1).addPet(PetType.HAMSTER, "Wuzzy", 1),
    new Person("John", "Doe")
);
```

#### Set

```java:Before
final Set<Integer> petAges = new HashSet<>();
for (final Person person : people)
{
    for (final Pet pet : person.getPets())
    {
        petAges.add(pet.getAge());
    }
}
```

```java:After
final MutableSet<Integer> petAges
            = people.flatCollect(Person::getPets).collect(Pet::getAge).toSet();
```

## 完了
最後に $ gradle test ですべての UnitTest を実行しましょう。達成感に浸れます。

<img width="669" alt="pet-kata.png" src="https://qiita-image-store.s3.amazonaws.com/0/102004/4de24dff-89af-f8be-6cfb-4d8b2d8c5f25.png">


---

# [company-kata](https://github.com/toastkidjp/eclipse-collections-kata/tree/toastkidjp_all_solved/company-kata/src/test/java/org/eclipse/collections/companykata)

## 前提
Company というクラスのオブジェクトが1つ(company)与えられています。

### Fields
suppliers の型が配列なのが気になるところです。

| Type | Name
|:---|:---
| MutableList<Customer> | customers 
| Supplier[] | suppliers 

### Methods
フィールドの getter が用意されており、メソッド参照で活用できます。また、Customer の持つ Order をすべて取得する getOrders() メソッドも用意されています。

## [Exercise1Test](https://github.com/toastkidjp/eclipse-collections-kata/blob/toastkidjp_all_solved/company-kata/src/test/java/org/eclipse/collections/companykata/Exercise1Test.java)
pet-kata を解いていれば特に難しいこともないでしょう。

### getCustomerNames

```java
final MutableList<String> customerNames = customers.collect(Customer::getName);
```

### getCustomerCities
```java
final MutableList<String> customerCities = customers.collect(Customer::getCity);
```

### getLondonCustomers
```java
final MutableList<Customer> customersFromLondon
            = customers.select(customer -> "London".equals(customer.getCity()));
```

## [Exercise2Test](https://github.com/toastkidjp/eclipse-collections-kata/blob/toastkidjp_all_solved/company-kata/src/test/java/org/eclipse/collections/companykata/Exercise2Test.java)
最初に Predicate を作り、それを使って基本的な Eclipse Collections のメソッドを動かす問題で構成されています。

### 事前準備
```java
private static final Predicate<Customer> CUSTOMER_FROM_LONDON
     = customer -> "London".equals(customer.getCity());
```

Customer クラスの TO_CITY も実装する必要があります。

```java
public static final Function<Customer, String> TO_CITY = Customer::getCity;
```

#### customerFromLondonPredicate
事前準備をしていると終わります。

#### doAnyCustomersLiveInLondon
anySatisfy を使います。

#### doAllCustomersLiveInLondon
allSatisfy を使います。

#### howManyCustomersLiveInLondon
count を使います。

#### getLondonCustomers
select を使います。

#### getCustomersWhoDontLiveInLondon
reject を使います。

#### getCustomersWhoDoAndDoNotLiveInLondon
select と reject の両方が必要な場合は partition を使うといい具合に書けます。

```java
final PartitionMutableList<Customer> partition
     = this.company.getCustomers().partition(CUSTOMER_FROM_LONDON);
final MutableList<Customer> customersFromLondon    = partition.getSelected();
final MutableList<Customer> customersNotFromLondon = partition.getRejected();
```

#### findMary & findPete
Company#getCustomerNamed(String) を実装するとパスするようになります。

```java
customers.detect(customer -> name.equals(customer.getName()));
```

## [Exercise3Test](https://github.com/toastkidjp/eclipse-collections-kata/blob/toastkidjp_all_solved/company-kata/src/test/java/org/eclipse/collections/companykata/Exercise3Test.java)
flatCollect の使い方を学びます。

### improveGetOrders
Company#getOrders() を実装します。

```java:Before
final MutableList<Order> orders = this.customers.flatCollect(Customer::getOrders);
for (final Customer customer : this.customers)
{
    orders.addAll(customer.getOrders());
}
return orders;
```

これがたった1行で書ける素晴らしさ……

```java:After
return this.customers.flatCollect(Customer::getOrders);
```

#### findItemNames

```java:findItemNames
final MutableList<LineItem> allOrderedLineItems
     = this.company.getOrders().flatCollect(Order::getLineItems);
final MutableSet<String> actualItemNames
     = allOrderedLineItems.collect(LineItem::getName).toSet();
```

#### findCustomerNames

```java
final MutableList<String> names = this.company.getCustomers().collect(Customer::getName);
```

## [Exercise4Test](https://github.com/toastkidjp/eclipse-collections-kata/blob/toastkidjp_all_solved/company-kata/src/test/java/org/eclipse/collections/companykata/Exercise4Test.java)
なぜか配列になっていた Supplier を使って、配列を Eclipse Collections でどう処理するかを学ぶ問題です。 Supplier とは供給業者の意で、 Java8 から追加された Supplier ではありません。

### findSupplierNames
company.getSuppliers() の戻り型を変えずに MutableList<String> へ変換するには ArrayIterate クラスを使います。第0引数に配列、第1引数に Predicate を受け取ります。

```java
final MutableList<String> supplierNames
            = ArrayIterate.collect(company.getSuppliers(), Supplier::getName);
```

### countSuppliersWithMoreThanTwoItems
ArrayIterate#count を使います。

```java
final Predicate<Supplier> moreThanTwoItems = supplier -> 2 < supplier.getItemNames().length;
final int suppliersWithMoreThanTwoItems = ArrayIterate.count(company.getSuppliers(), moreThanTwoItems);
```

### whoSuppliesSandwichToaster
supplier.getItemNames() の戻り型が array なので ArrayIterate を使って Predicate を作ります。

```java
final Predicate<Supplier> suppliesToaster = supplier -> ArrayIterate.anySatisfy(
                    supplier.getItemNames(), itemName -> "sandwich toaster".equals(itemName));
```

### filterOrderValues
orders が java.util.List なので、 factory の ofAll メソッドを使って変換します。
この問題ではテンプレートに従い、説明的な変数を用意して1段ずつ変形します。

```java
final MutableList<Double> orderValues
            = Lists.mutable.ofAll(orders).collect(Order::getValue);
final MutableList<Double> filtered = orderValues.select(value -> 1.5 < value);
```

ただ、私は下記のように1つにまとめるのが好きです。

```java
final MutableList<Double> filtered
            = Lists.mutable.ofAll(orders)
                .collect(Order::getValue)
                .select(value -> 1.5 < value);
```

変換をしない場合は下記のようにすれば同じ値を求められます。

```java
orders.stream()
            .map(Order::getValue)
            .filter(value -> 1.5 < value).collect(Collectors.toList())
```

### filterOrders
やはり ofAll で変換してから select します。

```java
final MutableList<Order> filtered
            = Lists.mutable.ofAll(orders).select(order -> 2.0 < order.getValue());
```

変換をしない場合は下記のようにすれば同じ値を求められます。

```java
orders.stream().filter(order -> 2.0 < order.getValue()).collect(Collectors.toList())
```

## [Exercise5Test](https://github.com/toastkidjp/eclipse-collections-kata/blob/toastkidjp_all_solved/company-kata/src/test/java/org/eclipse/collections/companykata/Exercise4Test.java)
先ほどと同じ問題かと思ったら、今度は Static Utilities を使ってはいけないようです。

### filterOrderValues
orders の 型を MutableList にして、Customer#getOrders の戻り型を MutableList にする(ためにフィールドの型も変えた)のですが、これでいいのでしょうか……

```java:Customer
private final MutableList<Order> orders = Lists.mutable.empty();

......

public MutableList<Order> getOrders()
```

```java
final MutableList<Order> orders = this.company.getMostRecentCustomer().getOrders();
final MutableList<Double> orderValues = orders.collect(Order::getValue);
```

#### Predicates.greaterThan(doubleValue)
より可読性が高まる Predicate 集があるらしいです。

### filterOrders
先ほどの変更をしてあれば、あとは1行修正するだけです。

```java
final MutableList<Order> filtered = orders.select(order -> 2.0 < order.getValue());
```

## [Exercise6Test](https://github.com/toastkidjp/eclipse-collections-kata/blob/toastkidjp_all_solved/company-kata/src/test/java/org/eclipse/collections/companykata/Exercise6Test.java)
sort や max などを使います。

### sortedTotalOrderValue
total を求めるメソッドはすでに用意されているので、total に変形させて、昇順を指定して sort するだけでよいです。

```java
final MutableList<Double> sortedTotalValues = company.getCustomers()
                .collect(Customer::getTotalOrderValue)
                .sortThis(Comparators.naturalOrder());
```

### maximumTotalOrderValue
totalOrderValue の List を作って max() メソッドを呼び出します。

```java
final Double maximumTotalOrderValue = company.getCustomers()
                .collect(Customer::getTotalOrderValue).max();
```

### customerWithMaxTotalOrderValue
sort を書いて最初の要素を取り出す、などということをしなくても maxBy メソッドを使えばよいです。

```java
final Customer customerWithMaxTotalOrderValue = company.getCustomers()
                .maxBy(Customer::getTotalOrderValue);
```

### supplierNamesAsTildeDelimitedString
ArrayIterate#collect で name だけにしてから makeString で連結します。 makeString メソッドの引数で delimiter を指定できます。省略すると「, 」が使われます。

```java
final String tildeSeparatedNames
            = ArrayIterate.collect(company.getSuppliers(), Supplier::getName).makeString("~");
```

### deliverOrdersToLondon
London 行きの Order だけ deliver します。forEach を使えとの指示だが、それだと曖昧だと言われるので each を使ってみます。

```java
this.company.getCustomers().select(customer -> "London".equals(customer.getCity()))
            .flatCollect(Customer::getOrders).each(Order::deliver);
```

もちろん指示違反なので、ちゃんと forEach を使います。曖昧だと言われているのでキャストで型を指定してやればよく、それをヒントにある通り Procedures#cast を使えば実現できます。

```java
this.company.getCustomers().select(customer -> "London".equals(customer.getCity()))
            .flatCollect(Customer::getOrders).forEach(Procedures.cast(Order::deliver));
```

## [Exercise7Test](https://github.com/toastkidjp/eclipse-collections-kata/blob/toastkidjp_all_solved/company-kata/src/test/java/org/eclipse/collections/companykata/Exercise7Test.java)
Multimap の使い方を学びます。

### customersByCity
groupBy でキーとなる要素を指定するだけで Multimap に変換できてしまいます。強力ですね。

```java
final MutableListMultimap<String, Customer> multimap
            = company.getCustomers().groupBy(Customer::getCity);
```

### mapOfItemsToSuppliers
従来のやり方から Multimap に適した形に直します。

```java:Before
final MutableMap<String, List<Supplier>> itemsToSuppliers = UnifiedMap.newMap();

        for (final Supplier supplier : this.company.getSuppliers())
        {
            for (final String itemName : supplier.getItemNames())
            {
                List<Supplier> suppliersForItem;
                if (itemsToSuppliers.containsKey(itemName))
                {
                    suppliersForItem = itemsToSuppliers.get(itemName);
                }
                else
                {
                    suppliersForItem = FastList.newList();
                    itemsToSuppliers.put(itemName, suppliersForItem);
                }

                suppliersForItem.add(supplier);
            }
        }
```

配列を使っている都合上、 flatCollect できずに Iterate in Iterate になって微妙だが、こんな感じになります。

```java:After
final MutableMultimap<String, Supplier> itemsToSuppliers = Multimaps.mutable.list.empty();

ArrayIterate.forEach(this.company.getSuppliers(), supplier -> {
    ArrayIterate.forEach(supplier.getItemNames(), itemName -> {
        itemsToSuppliers.put(itemName, supplier);
    });
});
```

### reminder
super クラス(CompanyDomainForKata) の setUpCustomersAndOrders をリファクタリングします。

```java
/**
         * TODO 7: Refactor Order and its API so this repetition is not necessary.
         */
        // TODO 7: Add 3 cups at 1.5 each to the order
        fredOrder.addLineItem(new LineItem("cup", 1.5));
        fredOrder.addLineItem(new LineItem("cup", 1.5));
        fredOrder.addLineItem(new LineItem("cup", 1.5));

        // TODO 7: Add 3 saucers at 1.0 each to the order
        fredOrder.addLineItem(new LineItem("saucer", 1.0));
        fredOrder.addLineItem(new LineItem("saucer", 1.0));
……省略……
```

上記のような雑なコピペを Interval で書き直す。oneTo を使うとよい。

```java
Interval.oneTo(3).each(i -> fredOrder.addLineItem(new LineItem("cup", 1.5)));
```

もちろん、for ループも書き換え可能です。

```java
for (int i = 0; i < 43; i++) {
    billOrder1.addLineItem(new LineItem("gnome", 7.50));
}
```

は下記のようにできます。

```java
Interval.oneTo(43).each(i -> billOrder1.addLineItem(new LineItem("gnome", 7.50)));
```

このリファクタリングに失敗すると次の問題が通らなくなるので注意しましょう。

## [Exercise8Test](https://github.com/toastkidjp/eclipse-collections-kata/blob/toastkidjp_all_solved/company-kata/src/test/java/org/eclipse/collections/companykata/Exercise8Test.java)
aggregateBy 等の少し難しめの問題が多いです。

### totalOrderValuesByCity
zeroValueFactory と aggregator を使って aggregateBy メソッドを呼び出します。

```java
final MutableMap<String, Double> map
            = company.getCustomers().aggregateBy(Customer::getCity, zeroValueFactory, aggregator);
```

### totalOrderValuesByItem
zeroValueFactory と aggregator を使って aggregateBy メソッドを呼び出します。

```java
final MutableMap<String, Double> map
            = company.getOrders().flatCollect(Order::getLineItems)
                .aggregateBy(LineItem::getName, zeroValueFactory, aggregator);
```

### sortedOrders
Javadoc コメントにやることが書いてあります。

> Find all customers' line item values greater than 7.5 and sort them by highest to lowest price.

1つずつ書いていくと

1. すべての Customer を取り出す
2. すべての LineItem を取り出す
3. value が 7.5 より上の要素を選択
4. 高い順にソート

flatCollect の 2連発＆ collect でなかなか遠回り感がありますが、1段ずつやった方が可読性ではよさそうです。 toSortedBag では sort アルゴリズムを選択できます。

```java
final MutableSortedBag<Double> orderedPrices
            = company.getCustomers()
                .flatCollect(Customer::getOrders)
                .flatCollect(Order::getLineItems)
                .collect(LineItem::getValue)
                .select(Predicates.greaterThan(7.5))
                .toSortedBag(Comparators.reverseNaturalOrder());
```

### whoOrderedSaucers
"saucer" を注文した Customer を取り出します。Customer を持っていないといけないのでこうなったのですが、微妙ですね……

```java
final MutableList<Customer> customersWithSaucers
            = company.getCustomers()
                .select(customer -> {
                    return customer.getOrders()
                            .flatCollect(Order::getLineItems)
                            .collect(LineItem::getName)
                            .anySatisfy(Predicates.in("saucer"));
                });
```

Predicates.in("saucer") は地味に便利です。

### ordersByCustomerUsingAsMap
MutableList#toMap(Function, Function) を使います。引数の2つの Function はそれぞれ key と value を取り出す操作を指定します。

```java
final MutableMap<String, MutableList<Order>> customerNameToOrders =
                this.company.getCustomers().toMap(Customer::getName, Customer::getOrders);
```

### mostExpensiveItem
groupBy で Multimap を作ります。key に最高の商品価格、 value に Customer を入れます。

```java
final MutableListMultimap<Double, Customer> multimap
            = company.getCustomers().groupBy(
                    customer -> customer.getOrders()
                                    .flatCollect(Order::getLineItems)
                                    .collect(LineItem::getValue)
                                    .max()
                                );
```

## 完了
$ gradle test して達成感に浸ります。

<img width="676" alt="company-kata.png" src="https://qiita-image-store.s3.amazonaws.com/0/102004/0b60627e-759f-8d0a-63f6-f6794de56355.png">