Skip to content

Instantly share code, notes, and snippets.

@loddar
Created October 27, 2018 16:01
Show Gist options
  • Save loddar/c236f56fdfeffee41bbb77643196ec28 to your computer and use it in GitHub Desktop.
Save loddar/c236f56fdfeffee41bbb77643196ec28 to your computer and use it in GitHub Desktop.
Q: How to handle state with java stream? A: Using Java Stream with reduce
package org.failearly.fp.learn;
import com.google.common.base.Verify;
import org.failearly.xquest.Question;
import org.javatuples.Triplet;
import org.junit.jupiter.api.Test;
import java.util.*;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.is;
@Question("How to handle state with java stream?")
class HandleStateInStreamTest {
@Test
void typicalWithTwoTokens() {
// given
var values = List.of("<a>", "val0", "<b>", "val2", "val3");
// when
final Map<String, List<String>> result = groupBy(values);
// then
assertThat("Grouped by <a> and <b>?", result, is(
Map.of(
"<a>", List.of("val0"),
"<b>", List.of("val2", "val3"))
)
);
}
@Question("What if the value list end with a token?")
@Test
void endsWithEmptyToken() {
// given
var values = List.of("<a>", "val0", "<b>", "val2", "val3","<c>");
// when
final Map<String, List<String>> result = groupBy(values);
// then
assertThat("Grouped by <a>, <b> and empty <c>?", result, is(
Map.of(
"<a>", List.of("val0"),
"<b>", List.of("val2", "val3"),
"<c>", List.of()
)
)
);
}
@Question("What if the list does not start with an token?")
@Test
void theFirstNoneTokenValuesWillBeIgnored() {
// given
var values = List.of("value w/o token","<a>", "val0", "<b>", "val2", "val3");
// when
final Map<String, List<String>> result = groupBy(values);
// then
assertThat("Values will be ignored?", result, is(
Map.of(
"<a>", List.of("val0"),
"<b>", List.of("val2", "val3"))
)
);
}
@Question("What if there are no values (thus empty list)?")
@Test
void emptyCase() {
// given
var values = List.<String>of();
// when
final Map<String, List<String>> result = groupBy(values);
// then
assertThat("Also empty map?", result, is(Map.of()));
}
@Question("What if there just one token?")
@Test
void mapWithOneEntry() {
// given
var values = List.of("<a>");
// when
final Map<String, List<String>> result = groupBy(values);
// then
assertThat("Map is not empty, but only one entry with empty list?", result, is(Map.of("<a>", List.of())));
}
@Question("What if there just two tokens?")
@Test
void mapWithTwoEntries() {
// given
var values = List.of("<a>","<b>");
// when
final Map<String, List<String>> result = groupBy(values);
// then
assertThat("Does the map has two entries?", result, is(
Map.of(
"<a>", List.of(),
"<b>", List.of())
)
);
}
private Map<String, List<String>> groupBy(final List<String> values) {
var result = values.stream().reduce(initial(), this::reduce, this::combine);
return finalize(result);
}
private Triplet<Map<String, List<String>>, Optional<String>, List<String>> initial() {
final Map<String, List<String>> map = new HashMap<>();
final Optional<String> token = Optional.empty();
final List<String> current = new LinkedList<>();
return new Triplet<>(map, token, current);
}
private Triplet<Map<String, List<String>>, Optional<String>, List<String>> reduce(final Triplet<Map<String, List<String>>, Optional<String>, List<String>> result, final String next) {
if( next.startsWith("<") && next.endsWith(">")) {
return result
.setAt1(Optional.of(next))
.setAt0(createNewMap(result, next))
.setAt2(List.of());
}
else {
final List<String> currentList = new LinkedList<>(result.getValue2());
currentList.add(next);
return result.setAt2(currentList);
}
}
private Map<String, List<String>> createNewMap(final Triplet<Map<String, List<String>>, Optional<String>, List<String>> result, final String next) {
final Map<String, List<String>> newMap = new HashMap<>(result.getValue0());
newMap.put(next, List.of());
result.getValue1().ifPresent(s -> newMap.put(s, result.getValue2()));
return Collections.unmodifiableMap(newMap);
}
private Triplet<Map<String, List<String>>, Optional<String>, List<String>> combine(final Triplet<Map<String, List<String>>, Optional<String>, List<String>> result, final Triplet<Map<String, List<String>>, Optional<String>, List<String>> ignored) {
Verify.verify(! ignored.getValue1().isPresent(), "Should never be set");
return result;
}
private Map<String,List<String>> finalize(Triplet<Map<String, List<String>>, Optional<String>, List<String>> result) {
final Map<String, List<String>> resultMap = new HashMap<>(result.getValue0());
result.getValue1().ifPresent(s -> resultMap.put(s, result.getValue2()));
return resultMap;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment