Garrett Smith
Garrett Smith

Reputation: 696

Java 8 Functional Programming avoid if conditional

How do you do the equivalent of the following transform() method using pure functional programming (without the if-conditional).

Meta: I'd appreciate a title edit, I'm not sure how to word this question in "functionalese"

public class Playground {

    private static Optional<Map<String,Integer>> transform(List<Tuple<String,Optional<Integer>>> input) {
        if (input.stream().anyMatch(t->t.second.isEmpty())) return Optional.empty();

        Map<String, Integer> theMap = input.stream()
                .map(t -> new Tuple<>(t.first, t.second.get()))
                .collect(Collectors.groupingBy(
                        t1 -> t1.first,
                        Collectors.mapping(t2 -> t2.second, toSingle())));
        return Optional.of(theMap);
    }

    @Test
    public void collect()  {
        List<Tuple<String,Optional<Integer>>> input1 = new ArrayList<>();
        input1.add(new Tuple<>("foo", Optional.of(1)));
        input1.add(new Tuple<>("bar", Optional.empty()));

        Optional<Map<String,Integer>> result1 = transform(input1);

        assertTrue(result1.isEmpty());

        List<Tuple<String,Optional<Integer>>> input2 = new ArrayList<>();
        input2.add(new Tuple<>("foo", Optional.of(1)));
        input2.add(new Tuple<>("bar", Optional.of(2)));

        Optional<Map<String,Integer>> result2 = transform(input2);

        assertTrue(result2.isPresent());
        assertEquals((int)1, (int)result2.get().get("foo"));
        assertEquals((int)2, (int)result2.get().get("bar"));
    }

    private static class Tuple<T1,T2> {
        public T1 first;
        public T2 second;
        public Tuple(T1 first, T2 second) {
            this.first = first;
            this.second = second;
        }
    }

    public static <T> Collector<T, ?, T> toSingle() {
        return Collectors.collectingAndThen(
                Collectors.toList(),
                list ->  list.get(0)
        );
    }
}

Upvotes: 0

Views: 801

Answers (3)

Holger
Holger

Reputation: 298153

“pure functional programming” is not necessarily a sign of quality and not an end in itself.

If you want to make the code simpler and more efficient, which may include getting rid of the if-conditional, especially as it bears a second iteration over the source data, you can do it in various ways. E.g.

private static <K,V> Optional<Map<K,V>> transform(List<Tuple<K,Optional<V>>> input) {
    final class AbsentValue extends RuntimeException {
        AbsentValue() { super(null, null, false, false); }
    }

    try {
        return Optional.of(input.stream().collect(Collectors.toMap(
            t1 -> t1.first,
            t2 -> t2.second.orElseThrow(AbsentValue::new),
            (first,next) -> first)));
    } catch(AbsentValue av) {
        return Optional.empty();
    }
}

When empty optionals are truly the exceptional case, you can make flagging via exception part of the method’s contract, e.g.

public static class AbsentValueException extends RuntimeException {

}
private static <K,V> Map<K,V> transform(List<Tuple<K,Optional<V>>> input)
    throws AbsentValueException {

    return input.stream().collect(Collectors.toMap(
        t1 -> t1.first,
        t2 -> t2.second.orElseThrow(AbsentValueException::new),
        (first,next)->first));
}
@Test(expected = AbsentValueException.class)
public void collect1() {
    List<Tuple<String,Optional<Integer>>> input1 = new ArrayList<>();
    input1.add(new Tuple<>("foo", Optional.of(1)));
    input1.add(new Tuple<>("bar", Optional.empty()));

    Map<String,Integer> result1 = transform(input1);
}

@Test
public void collect2() {
    List<Tuple<String,Optional<Integer>>> input2 = new ArrayList<>();
    input2.add(new Tuple<>("foo", Optional.of(1)));
    input2.add(new Tuple<>("bar", Optional.of(2)));

    Map<String,Integer> result2 = transform(input2);

    assertEquals((int)1, (int)result2.get("foo"));
    assertEquals((int)2, (int)result2.get("bar"));
}

Even better would be not to put optionals into the list of tuples in the first place.

Upvotes: 1

ArmDuke
ArmDuke

Reputation: 108

Although my solution does not satisfy your result, I can offer a solution with the ternary operator

private static Map<String, Integer> transform(List<Tuple<String, Optional<Integer>>> input) {
    return input.stream().anyMatch(t -> t.second.isEmpty()) ? Collections.emptyMap() :
            input.stream()
                    .map(t -> new Tuple<>(t.first, t.second.get()))
                    .collect(Collectors.groupingBy(
                            t1 -> t1.first,
                            Collectors.mapping(t2 -> t2.second, toSingle())));
}

Upvotes: 1

Utku &#214;zdemir
Utku &#214;zdemir

Reputation: 7725

This might work for you:

  private static Optional<Map<String, Integer>> transform(
      List<Tuple<String, Optional<Integer>>> input) {
    return Optional.of(input)
        .filter(t -> t.stream().allMatch(a -> a.second.isPresent()))
        .map(
            in ->
                in.stream()
                    .filter(t -> t.second.isPresent())
                    .map(t -> new Tuple<>(t.first, t.second.get()))
                    .collect(
                        Collectors.groupingBy(
                            t1 -> t1.first, Collectors.mapping(t2 -> t2.second, toSingle()))));
  }

Upvotes: 1

Related Questions