【Java8】大中小カテゴリを持つリストから項目取得

# はじめに
StreamAPIのfilterとreduceのお試し。
以下をEnumとStreamAPIで実装をしてみました。

- 大中小のカテゴリコードを持つマスタがある。
- 大中小を指定して項目を返す。
- 大中配下の小の合計値を返す。

# 実装
## カテゴリマスタ(Enum)
こんな感じで大中小を持つカテゴリマスタを用意します。

```java:Category.java
package com.example.demo.categoryfilter;

public enum Category {
	EMPLOYEE_111(1,1,1), // 1部1課　社員1
	EMPLOYEE_112(1,1,2), // 1部1課　社員2
	EMPLOYEE_113(1,1,3), // 1部1課　社員3
	EMPLOYEE_121(1,2,1), // 1部2課　社員1
	EMPLOYEE_122(1,2,2), // 1部2課　社員2
	EMPLOYEE_123(1,2,3), // 1部2課　社員3
	EMPLOYEE_211(2,1,1), // 2部1課　社員1
	EMPLOYEE_212(2,1,2), // 2部1課　社員2
	;
	Category(int large,int medium,int small){
		this.large=large;
		this.medium=medium;
		this.small=small;
	}
	private int large; // 部コード
	private int medium; // 課コード
	private int small; // 社員コード
	public int getL() {
		return large;
	}
	public void setL(int large) {
		this.large = large;
	}
	public int getM() {
		return medium;
	}
	public void setM(int medium) {
		this.medium = medium;
	}
	public int getS() {
		return small;
	}
	public void setS(int small) {
		this.small = small;
	}
}
```


## Filterメソッド

```java:CategoryFilter.java
package com.example.demo.categoryfilter;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.List;
import java.util.Optional;

public class CategoryFilter {

	/**
	 * 部署と課と社員コードを指定して社員名を返す。
	 * 
	 * @param list
	 * @param category
	 * @return 社員名
	 */
	public static String filter(List<Employee> list, Category category) {
		Optional<String> ret = list.stream()
				.filter(e -> e.getL() == category.getL())
				.filter(e -> e.getM() == category.getM())
				.filter(e -> e.getS() == category.getS())
				.map(e -> e.getName())
				.findFirst();
		return ret.orElse("名無しさん");
	}

	/**
	 * 部署と課を指定して配下の社員の給与を合計する。
	 * BigDecimalの処理はおまけ。
	 * 
	 * @param list
	 * @param category
	 * @return 給与合計
	 */
	public static BigDecimal sum(List<Employee> list, Category category) {
		Optional<BigDecimal> ret = list.stream()
				.filter(e -> e.getL() == category.getL())
				.filter(e -> e.getM() == category.getM())
				.map(e -> e.getSalary())
				.reduce((sum, i) -> sum.add(i));
		// 初期値０、小数点以下切り捨てで返す
		return ret.orElse(BigDecimal.ZERO).setScale(0, RoundingMode.DOWN);
	}
}
```

## Employee

```java:Employee.java
package com.example.demo.categoryfilter;

import java.math.BigDecimal;

import lombok.Data;

@Data
public class Employee {
	private int l;
	private int m;
	private int s;
	private String name;
	private BigDecimal salary;

	public Employee(Category category, String name, BigDecimal salary) {
		this.l = category.getL();
		this.m = category.getM();
		this.s = category.getS();
		this.name = name;
		this.salary = salary;
	}
	public Employee(Category category, String name, int salary) {
		this(category, name, new BigDecimal(salary));
	}
}
```

## テストコード
七郎さんと八郎さんは異常系用。

```java:CategoryFilterTests.java
package com.example.demo.categoryfilter;

import static org.hamcrest.CoreMatchers.is;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;

import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;

@RunWith(JUnit4.class)
public class CategoryFilterTests {
	private static List<Employee> list;

	@BeforeClass
	public static void before() {
		list = sample();
	}

	@Test
	public void test_filter1() {
		String name = CategoryFilter.filter(list, Category.EMPLOYEE_123);
		System.out.println("EMPLOYEE_123 name=" + name);
		Assert.assertThat(name, is("山田六郎"));
	}

	@Test
	public void test_filter2() {
		String name = CategoryFilter.filter(list, Category.EMPLOYEE_212);
		System.out.println("EMPLOYEE_212 name=" + name);
		Assert.assertThat(name, is("名無しさん"));
	}

	@Test
	public void test_sum1() {
		BigDecimal sum = CategoryFilter.sum(list, Category.EMPLOYEE_122);
		System.out.println("EMPLOYEE_122 sum=" + sum);
		Assert.assertThat(sum, is(new BigDecimal(600_000)));
	}

	@Test
	public void test_sum2() {
		BigDecimal sum = CategoryFilter.sum(list, Category.EMPLOYEE_212);
		System.out.println("EMPLOYEE_21 sum=" + sum);
		Assert.assertThat(sum, is(new BigDecimal(0)));
	}

	private static List<Employee> sample() {
		List<Employee> list = new ArrayList<>();
		list.add(new Employee(Category.EMPLOYEE_111, "山田一郎", 111_000));
		list.add(new Employee(Category.EMPLOYEE_112, "山田二郎", 112_000));
		list.add(new Employee(Category.EMPLOYEE_113, "山田三郎", 113_000));
		list.add(new Employee(Category.EMPLOYEE_121, "山田四郎", 100_000));
		list.add(new Employee(Category.EMPLOYEE_122, "山田五郎", 200_000));
		list.add(new Employee(Category.EMPLOYEE_123, "山田六郎", 300_000));
//		list.add(new Employee(Category.EMPLOYEE_211, "山田七郎", 211_000));
//		list.add(new Employee(Category.EMPLOYEE_212, "山田八郎", 212_000));
		list.forEach(System.out::println);
		System.out.println();
		return list;
	}
}
```

## 実行結果

```:ログ
Employee(l=1, m=1, s=1, name=山田一郎, salary=111000)
Employee(l=1, m=1, s=2, name=山田二郎, salary=112000)
Employee(l=1, m=1, s=3, name=山田三郎, salary=113000)
Employee(l=1, m=2, s=1, name=山田四郎, salary=100000)
Employee(l=1, m=2, s=2, name=山田五郎, salary=200000)
Employee(l=1, m=2, s=3, name=山田六郎, salary=300000)

EMPLOYEE_123 name=山田六郎
EMPLOYEE_212 name=名無しさん
EMPLOYEE_122 sum=600000
EMPLOYEE_21 sum=0
```

# あとがき
やっぱりstream apiはキレイですねー。