Home > Back-end >  Transform list of Either into list of left and list of right
Transform list of Either into list of left and list of right

Time:06-06

Vavr's Either seems to solve one of my problems were some method does a lot of checks and returns either CalculationError or CalculationResult.

Either<CalculationError, CalculationResult> calculate (CalculationData calculationData) {
// either returns Either.left(new CalculationError()) or Either.right(new CalculationResult())

}

I have a wrapper which stores both errors and results

class Calculation {
 List<CalculationResult> calculationResults;
 List<CalculationError> calculationErrors;
}

Is there any neat solution to transform stream from Collection<CalculationData> data to Calculation?

CodePudding user response:

This can be easily done using a custom collector. With a bit of pseudo code representing the Either:

Collector<Either<CalculationError, CalculationResult>, ?, Calculation> collector = Collector.of(
        Calculation::new,
        (calc, either) -> {
            if (either has error) {
                calc.calculationErrors.add(either.error);
            } else {
                calc.calculationResults.add(either.result);
            }
        },
        (calc1, calc2) -> {
            calc1.calculationErrors.addAll(calc2.calculationErrors);
            calc1.calculationResults.addAll(calc2.calculationResults);
            return calc1;
        }
);

Calculation calc = data.stream()
        .map(this::calculate)
        .collect(collector);

Note that Calculation should initialize its two lists (in the declaration or a new constructor).

CodePudding user response:

Well, you're using vavr, so 'neat' is right out. Tends to happen when you use tools that are hostile to the idiomatic form of the language. But, then again, 'neat' is a nebulous term with no clear defined meaning, so, I guess, whatever you think is 'neat', is therefore 'neat'. Neat, huh?

Either itself has the sequence method - but both of them work the way Either is supposed to work: They are left-biased in the sense that any Lefts present is treated as erroneous conditions, and that means all the Right values are discarded if even one of your Eithers is a Left. Thus, you cannot use either of the sequence methods to let Either itself bake you a list of the Right values. Even sequenceRight won't do this for you (it stops on the first Left in the list and returns that instead). The filter stuff similarly doesn't work like that - Either very much isn't really an Either in the sense of what that word means if you open a dictionary: It does not mean: A homogenous mix of 2 types. It's solely a non-java-like take on exception management: Right contains the 'answer', left contains the 'error' (you're using it correctly), but as a consequence there's nothing in the Either API to help with this task - which in effect involves 'please filter out the errors and then do something' ("Silently ignore errors" is rarely the right move. It is what is needed here, but it makes sense that the Either API isn't going to hand you a footgun. Even if you need it here).

Thus, we just write it plain jane java:

var calculation = new Calculation();
for (var e : mix) {
  if (e.isLeft()) calculation.calculationErrors.add(e.getLeft());
  if (e.isRight()) calculation.calculationResult.add(e.getRight());
}

(This presumes your Calculation constructor at least initializes those lists to empty mutables).

NB: Rob Spoor's answer also assumes this and is much, much longer. Sometimes the functional way is the silly, slow, unwieldy, hard to read, way.

NB2: Either.sequence(mix).orElseRun(s -> calculation.errors = s.asJava()); is a rather 'neat' way (perhaps - it's in the eye of the beholder) of setting up the errors field of your Calculation class. No joy for such a 'neat' trick to fill the 'results' part of it all, however. That's what the bulk of my answer is trying to explain: There is no nice API for that in Either, and it's probably by design, as that involves intentionally ignoring the errors in the list of Eithers.

  • Related