Using Java, I am trying to find a clean way to accumulate multiple different value in a series of lambda. For a concrete example see this following piece of JS (typescript) code:
// some filtering helpers. not really interested in that because there are similar things in Java const mapFilter = <T,U>(arr: T[], transform: (item: T, idx: number, arr: T[]) => U) => arr.map(transform).filter(Boolean) const mapFilterFlat = <T,U>(arr: T[], transform: (item: T, idx: number, arr: T[]) => U[]) => mapFilter(arr, transform).flat() const findDeep = () => mapFilterFlat(someObj.level1Items, A => mapFilterFlat(A.level2Items, B => mapFilter(B.level3Items, C => // I am able to access closure variables so i can push them all in my result, instead of just the last level C == something ? ({A, B, C}) : null ))) let found: {A: any, B: any, C: any}[] = findDeep();
I am not sure if there are existing Java Stream APIs for accumulate such a result. Maybe it’s not really possible and i should look into another JVM language ?
I eventually did this, but it’s not really concise (although i know Java is not really):
public class Finder implements BiFunction<SomeObj, Predicate<SomeObj>, List<State>> { static class State { Integer A; String B; List C; static State from(Map<String, Object> inputs) { var res = new State(); res.A = (Integer) inputs.get("A"); res.B = (String) inputs.get("B"); res.C = (List) inputs.get("C"); return res; } } Map<String, Object> fields; <T> T store(String key, T value) { return (T) fields.put(key, value); } public List<State> apply(SomeObj someObj, Predicate<C> predicate) { fields = new HashMap<>(); return config.level1Items .stream() .flatMap(A -> store("A", A).level2Items.stream()) .flatMap(B -> store("B", B).level3Items.stream()) .peek(C -> store("C", C)) .filter(predicate) .map(o -> State.from(fields)) .collect(Collectors.toList()); } }
I am not even sure that the BiFunction implementation is useful. Thanks for your guidances
Advertisement
Answer
You are translating TypeScript but you are no translating as it was: you are not “inheriting” the depth of lambda, there are all at the same level and they all don’t see the variable from their parent context.
const findDeep = () => mapFilterFlat(someObj.level1Items, A => mapFilterFlat(A.level2Items, B => mapFilter(B.level3Items, C => // I am able to access closure variables so i can push them all in my result, instead of just the last level C == something ? ({A, B, C}) : null )))
This is not the same as:
return config.level1Items .stream() .flatMap(A -> store("A", A).level2Items.stream()) .flatMap(B -> store("B", B).level3Items.stream()) .peek(C -> store("C", C)) .filter(predicate) .map(o -> State.from(fields)) .collect(Collectors.toList());
This should be something like this:
return config.level1Items .stream() .flatMap(A -> store("A", A).level2Items .stream() .flatMap(B -> store("B", B).level3Items .stream()) ) .peek(C -> store("C", C)) // the same must be done here .filter(predicate) .map(o -> State.from(fields)) .collect(Collectors.toList());
If I try to understand your algorithm, you are trying to get all permutation of {A, B, C} where C = something: your code should be something like this, using forEach
to iterate over items of Collection
/Iterator
.
List<Triple<A,B,C>>> collector = new ArrayList<>(); config.level1Items.forEach(a -> { a.level2Items.forEach(b -> { b.level3Items.forEach(c -> { if (c.equals(something)) { collector.add(new Triple<>(a, b, c)); } } }); });
You don’t need a stream for that.
Triple is simply an implementation of tuple of 3 value, for example the one at commons-lang3.