java – how to remove trinocular operator in Java8 Stream filter

I’m practicing refactoring and Java 8 with very simple examples of Powerball.

I used LottoRank enum to write a function that returns the matched LottoRank based on the number of matches and bonus balls.

  • The 1st place is when you hit all 6 numbers.

  • The 2nd place is when you hit 5 with bonus ball.

  • The 3rd place is when you hit 5 without bonus ball.

  • The 4th place is when you hit 4 numbers.

  • The 5th place is when you hit 3 numbers.

The logic for obtaining this value is as follows.

NONE(Integer.MIN_VALUE, 0),
  FIFTH(3, 5000),
  FOURTH(4, 50000),
  THIRD(5, 1500000),
  SECOND(5, 30000000),
  FIRST(6, 2000000000);

  private final int matchCount;
  private final Money winnerMoney;

  LottoRank(int matchCount, int winnerMoney) {
    this.matchCount = matchCount;
    this.winnerMoney = new Money(winnerMoney);
  }

  public boolean isCorrectMatchCount(int matchCount) {
    return this.matchCount == matchCount;
  }

  public int calculateWinningMoney(Integer value) {
    return winnerMoney.multiple(value);
  }

  public static LottoRank findRank(String winnerRank) {
    return LottoRankPredicates.filterLottoRankWithString(winnerRank);
  }

  public static LottoRank valueOf(int matchCount, boolean bonusBall) {
    return Arrays.stream(LottoRank.values())
        .filter(rank -> isThirdOrSecond(matchCount) ?
            filterIsSecond(bonusBall, rank.winnerMoney) : rank.matchCount == matchCount)
        .findAny()
        .orElse(LottoRank.NONE);
  }

  private static boolean isThirdOrSecond(int matchCount) {
    return matchCount == LottoRank.THIRD.matchCount;
  }

  private static boolean filterIsSecond(boolean bonusBall, Money winnerMoney) {
    return bonusBall ? winnerMoney.equals(LottoRank.SECOND.winnerMoney) : winnerMoney.equals(LottoRank.THIRD.winnerMoney);
  }

  public static LottoRank matches(List<Number> winningNumbers, List<Number> holdingLottoNumbers, Number bonusBall) {
    int matchCount =  Math.toIntExact(winningNumbers.stream()
        .filter(holdingLottoNumbers::contains)
        .count());
    boolean isSecond = holdingLottoNumbers.contains(bonusBall);
    return LottoRank.valueOf(matchCount, isSecond);
  }

If you look at the filter part of valueOf(), you will determine whether it is 2nd or 3rd (i.e., you got 5 correct). Based on whether the bonus ball has been hit or not, the logic is updated to 2nd place and if not, the ranking is processed according to the correct number.

I want to erase the trinomial operator in this part.

However, else statements and switch-case statements are not allowed.

I thought of a prefix or multiple filters, but eventually the first condition must be correct to run on the next condition, and this code will be generated.

It’s a code that didn’t work properly, but it’s like this.

  public static LottoRank valueOf(int matchCount, boolean bonusBall) {
    return Arrays.stream(LottoRank.values())
        .filter(rank -> matchCount == rank.matchCount)
        .filter(rank -> isThirdOrSecond(matchCount))
        .filter(rank -> filterIsSecond(bonusBall, rank.winnerMoney))
        .findAny()
        .orElse(LottoRank.NONE);
  }

Is there a way to refactory this more?

Edited

I was thinking about the above and i processed it with the LottoRankPredicates class as below.

I wonder what you think about refactoring like this.

public class LottoRankPredicates {

  private static Predicate<LottoRank> isSecondOrThird(boolean bonusBall) {
    return rank -> getSecondOrThird(bonusBall, rank);
  }

  private static Predicate<LottoRank> defaultCase(int matchCount) {
    return rank -> rank.isCorrectMatchCount(matchCount);
  }

  private static Predicate<LottoRank> findRank(String winnerRank) {
    return rank -> rank.name().equals(winnerRank);
  }

  private static boolean getSecondOrThird(boolean bonusBall, LottoRank rank) {
    if(bonusBall) {
      return rank == LottoRank.SECOND;
    }
    return rank == LottoRank.THIRD;
  }

  public static LottoRank filterLottoRankWithString(String winnerRank) {
    return Arrays.stream(LottoRank.values())
        .filter(findRank(winnerRank))
        .findAny()
        .orElseThrow(IllegalArgumentException::new);
  }

  public static LottoRank filterLottoRankIsSecondOrThird(boolean bonusBall) {
    return Arrays.stream(LottoRank.values())
        .filter(isSecondOrThird(bonusBall))
        .findAny()
        .orElse(LottoRank.NONE);
  }

  public static LottoRank filterLottRankIsDefault(int matchCount) {
    return Arrays.stream(LottoRank.values())
        .filter(defaultCase(matchCount))
        .findAny()
        .orElse(LottoRank.NONE);
  }
}
public static LottoRank findRank(String winnerRank) {
    return LottoRankPredicates.filterLottoRankWithString(winnerRank);
  }

  public static LottoRank valueOf(int matchCount, boolean bonusBall) {
    if(isThirdOrSecond(matchCount)) {
      return LottoRankPredicates.filterLottoRankIsSecondOrThird(bonusBall);
    }
    return LottoRankPredicates.filterLottRankIsDefault(matchCount);
  }

  private static boolean isThirdOrSecond(int matchCount) {
    return matchCount == LottoRank.THIRD.matchCount;
  }