pamiers
pamiers

Reputation: 355

Can I put a condition on the lambda expression in the reduce operation?

Can I skip certain operations on the first stream in a lambda expression? For example

List<BeanClass> fetch = getBeanClsss();
BeanClass bean = fetch.stream().reduce(newBean,     (p1,p2)-> { 
p1.setVal(p1.getVal() + p2.getVal());
// if first element skip
// else
p1.setValNum(p1.getValNum() + p2.getValNum());
return p1;
});

At first element, I do not want to run p1.setValNum method but p1.setVal method to run. So I want to do everything method except the first one.

Upvotes: 4

Views: 2871

Answers (4)

123-xyz
123-xyz

Reputation: 637

I assume you're not going to execute it in parallel stream(if you do, please be careful with p1.setValNum(p1.getValNum() + p2.getValNum())), Here is the solution by StreamEx with fluent setters: The code is explained by itself.

StreamEx.of(fetch).mapFirst(b -> newBean.setVal(newBean.getVal() + b.getVal()))
                  .skip(1)
                  .reduce((a, b) -> a.setValNum(a.getValNum() + b.getValNum()));

BeanClass with fluent setters:

public static class BeanClass {
    private int val;
    private int valNum;
    public int getVal() {
        return val;
    }
    public BeanClass setVal(int val) {
        this.val = val;
        return this;
    }
    public int getValNum() {
        return valNum;
    }

    public BeanClass setValNum(int valNum) {
        this.valNum = valNum;
        return this;
    }
    // ...
}

Upvotes: 0

Eugene
Eugene

Reputation: 120978

Assuming val and valNum are positive numbers and sum will not overflow:

Well if I understood correctly, than the very first time you reduce, than this will hold: p1 == newBean. In order for the reduce to work correctly you need to respect the identity property, what this means that newBean has to have val and valNum set to zero.

That means that if you do p1.getVal() and see that it's zero, it means that p2 is actually the first element from your stream.

Also notice that you are violating reduce here - you need to return a new instance from the reduce method, something like:

 .reduce(newBean, (p1, p2) -> {
     BeanClass beanClass = new BeanClass();

     // the first time you reduce
     if(p1.getVal() == 0){
           beanClass.setVal(p1.getVal() + p2.getVal());     
     }else {
           beanClass.setVal(p1.getVal() + p2.getVal());
           beanClass.setValNum(p1.getValNum() + p2.getValNum());
     }
     return beanClass;  
 })

Ultimately what you want to do is have a BeanClass instance that has val being equal to sum all other val from the Stream, except the first one and valNum being equal to the sum of all valNum form the Stream. If so, streaming twice then creating the instance is much more simpler with Misha's solution.

Upvotes: 0

Misha
Misha

Reputation: 28163

Don't use reduce for mutable reduction. If the stream were ever made parallel, you would get unpredictable results. You didn't specify the return type of getVal and getValNum, so I will suppose they both return an int. If you want to clarify your question, I will adjust the answer accordingly.

int sumVal = fetch.stream()
                      .mapToInt(BeanClass::getVal)
                      .sum();

int sumValNum = fetch.stream()
                      .skip(1)  // now skipping the first one is straightforward!
                      .mapToInt(BeanClass::getValNum)
                      .sum();

newBean.setVal(newBean.getVal() + sumVal);

newBean.setValNum(newBean.getValNum() + sumValNum);

Upvotes: 1

Naman
Naman

Reputation: 32028

You can use skip() over Stream as:

fetch.stream().skip(1).reduce(newBean,     (p1,p2)-> { 
    ...perform your actions
});

Returns a stream consisting of the remaining elements of this stream after discarding the first n elements of the stream. If this stream contains fewer than n elements then an empty stream will be returned.

Upvotes: 0

Related Questions