Madhur Bansal
Madhur Bansal

Reputation: 63

Converting enhanced loop into Java 8 Streams, filter

I am trying to learn Java 8. Is there a way to turn the method below into Java 8 Streams, filter, and forEach. If so, how?

String[] couponList = coupons.split(",");

for(String coupon:couponList) {
  singleCouponUsageCount = getSingleCouponUsageCount(coupon);
  if(singleCouponUsageCount >= totalUsageCount)
    return 0;
}
return 1;

//

for(String coupon:couponList) {
  singleCouponUsageCount = getSingleCouponUsageCount(coupon);
  if(singleCouponUsageCount >= totalUsageCount)
    return singleCouponUsageCount;
}
return singleCouponUsageCount;

Upvotes: 2

Views: 439

Answers (6)

Naman
Naman

Reputation: 31878

Given the code that you've shared. An important utility for you would be to create a lookup map for coupon usage.

Map<String, Long> couponUsageCount(String[] couponList) {
    return Arrays.stream(couponList)
            .collect(Collectors.toMap(Function.identity(), 
                      coupon ->> getSingleCouponUsageCount(coupon)));
}

Further, it eases to incorporate this into the other two implementations.

// note: boolean instead of 0 and 1
boolean countUsageExceeds(String[] couponList, Long totalUsageCount) {
    return couponUsageCount(couponList).values()
            .stream()
            .anyMatch(usage -> usage >= totalUsageCount);
}


// better to use Optional since you might not find any such value
// same as above method returning false
Optional<Long> exceededValue(String[] couponList, Long totalUsageCount) {
    Map<String, Long> couponUsageCount = couponUsageCount(couponList);
    // use this with orElse if you want to return an absolute value from this method
    long lastValue = couponUsageCount.get(couponList[couponList.length - 1]);
    return couponUsageCount.values()
            .stream()
            .filter(usage -> usage >= totalUsageCount)
            .findFirst();
}

Upvotes: 0

Holger
Holger

Reputation: 298153

Usually, you want a search operation to be short-circuiting, in other words, to return immediately when a match has been found. But unlike operations like collect, the short-circuiting operations of the Stream API can’t be customized easily.

For you specific operation, you can split the operation into two, which can still be formulated as a single expression:

String[] couponList = coupons.split(",");

return Arrays.stream(couponList, 0, couponList.length-1)
    .map(coupon -> getSingleCouponUsageCount(coupon))
    .filter(singleCouponUsageCount -> singleCouponUsageCount >= totalUsageCount)
    .findFirst()
    .orElseGet(() -> getSingleCouponUsageCount(couponList[couponList.length-1]));

This does a short-circuiting search over all but the last element, returning immediately when a match has been found. Only if no match has been found there, the last element will be processed and its result returned unconditionally.

Upvotes: 4

Lino
Lino

Reputation: 19926

You can also use Pattern.splitAsStream() for this, which directly returns a Stream:

// as a constant
private static final Pattern COMMA = Pattern.compile(",");

// somewhere else in a method
boolean found = COMMA.splitAsStream(coupons)
    // effectively the same as coupon -> getSingleCouponCount(count)
    .map(this::getSingleCouponCount)
    .anyMatch(count -> count >= totalUsageCount);

return found ? 0 : 1;

Upvotes: 0

Kenna
Kenna

Reputation: 302

Yes, you can do it with streams:

List<Integer> results = couponList
    .stream()
    .map(coupon -> getSingleCouponUsageCount(coupon))
    .filter(count -> count >= totalUsageCount ? 0 : 1)
    .collect(Collectors.toList());

Upvotes: 0

Eran
Eran

Reputation: 393811

You can Stream over the elements of the array, map them to their usage count, and use anyMatch to determine if any of the usage counts meets the criteria that should result in returning 0:

return Arrays.stream(coupons.split(","))
             .map(coupon -> getSingleCouponUsageCount(coupon))
             .anyMatch(count -> count >= totalUsageCount) ? 0 : 1;

EDIT:

For you second snippet, if you want to return the first count that matches the condition, you can write:

return Arrays.stream(coupons.split(","))
             .map(coupon -> getSingleCouponUsageCount(coupon))
             .filter(count -> count >= totalUsageCount)
             .findFirst()
             .orElse(someDefaultValue);

Upvotes: 5

b.GHILAS
b.GHILAS

Reputation: 2303

You can do it like this

return Stream.of(coupons.split(","))
      .anyMatch(coupon -> getSingleCouponUsageCount(coupon) >= totalUsageCount) ? 0 : 1;

Upvotes: 1

Related Questions