I'm not sure what to call the property that's 2 layers deep.
But let's say we have List<FruitColor> where Fruit and Color are 2 entities. (These are all example entities) A fruit can have different colors, but colors can also have different fruits.
public class FruitColor {
    private String fruitColorId;
    private Fruit fruit;
    private Color color;
    private int ripe; //1 to 3 where 1 unripe 2 normal 3 ripe
    // more props, getters, etc.
}
public class Fruit {
    private String fruitId;
    private String fruitName;
    // getters, etc.
}
public class Color {
    private String colorId;
    private String colorName;
    // getters, etc.
}
I'm thinking it in the following steps:
- Filter all the duplicates by fruitName
- Pick one of the two 'duplicates'. I say 'duplicates' because they only have the same name, but not the samecolor. The rule to choose which one to keep is the season/time-transition indicatorsystemTransition, which is described by thedoublepropertysystemTransitionthat ranges from0to a100.
- Remove the other 'duplicate'.
- If there is a duplicate, makes the fruit less ripe by 0.5
- Set the factorialRipeness, which isripe * systemTransitionorripe * (100 - systemTransition)depending on how far we are in the season, halfway is the threshold.
- Return suggested FruitColorswith the rightColor, sorted byFactorialRipenessThe thing is, this is probably do-able with a lot of for loops, but I'm just wondering if there are any ways that are more efficient.
I did find this source on how to filter by property, but this solution doesn't let me filter on the property of a property(=fruitName).
Java 8 Distinct by property
I've made a code example (again different entities) that works. Are there any parts that could be made more efficient? By more efficient I mean, if the code could be shorter and/or faster. I'll definitely refactor the method into smaller parts later. Sorry for the confusing bits, this problem is really hard to explain without using the exact same entities.
//logic in the method
{   
    List<FruitColor> fruitColors = getAllFCs();
    
    //Remove if FC is neither colors that were chosen
    fruitColors.removeIf(fc -> fc.getColor().getColorId() != presentColor.getColor().getColorId()
            && fc.getColor().getColorId() != futureColor.getColor().getColorId());
    List<FruitColor> suggestedFruits = new ArrayList<>();
    //Systemtransition is the season/time from 0 to 100, where 100 the fruit should be completely ripe with the corresponding color.
    //If the time is less than half, it should pick the present color, which is the less ripe color.
    boolean getPresentColorFruit = systemTransition < 50;
    // --->This whole for-if-if-else-elseif-if-else part I wanted to make more "efficient" <---
    for (FruitColor fruitColor : fruitColors) {
        //First check for duplicate FCs 
        List<FruitColor> sameNameFruits = fruitColors.stream()
                .filter(fc -> fc.getFruit().getName().equals(itemColor.getFruit().getName()))
                .collect(Collectors.toList());
                
        //If there is only one of the fruit, check if it's added already. If not, than add with proper ripeness
        //FactorialRipeness is the "true" ripeness.
        if (sameNameFruits.size() == 1 && !suggestedFruits.stream().anyMatch(fc -> fc.getFruit().getName().equals(sameNameFruits.get(0).getFruit().getName()))) {
            FruitColor onlyOne = sameNameFruits.get(0);
            if (onlyOne.getColor().getColorId() == presentColor.getColor().getColorId()) {
                onlyOne.setFactorialRipeness(onlyOne.getRipeness() * systemTransition);
            } else {
                onlyOne.setFactorialRipeness(onlyOne.getRipeness() * (100 - systemTransition));
            }
            suggestedFruits.add(onlyOne);
          // If there are multiple FCs, than the ripeness has to go down. Which prioritizes duplicate FCs more. (this part isn't logical with these entities, sorry)
        } else if(!suggestedFruits.stream().anyMatch(fc -> fc.getFruit().getName().equals(sameNameFruits.get(0).getFruit().getName()))){
            if (getPresentColorFruit) {
                FruitColor fcWithPresentColor = sameNameFruits.stream()
                        .filter(fc -> fc.getColor().getColorId() == presentColor.getColor().getColorId()).findFirst()
                        .get();
                fcWithPresentColor.setFactorialRipeness((fcWithPresentColor.getRipeness() - 0.5) * systemTransition);
                suggestedFruits.add(fcWithPresentColor);
            } else {
                FruitColor fcWithFutureColor = sameNameFruits.stream()
                        .filter(fc -> fc.getColor().getColorId() == futureColor.getColor().getColorId()).findFirst()
                        .get();
                //This part is also not logical, but if the presentColor is not chosen. Than we need to multiply by the opposite amount of the systemTransition
                fcWithFutureColor.setFactorialRipeness((fcWithFutureColor.getRipeness() - 0.5) * (100 - systemTransition)); 
                suggestedFruits.add(fcWithFutureColor);
            }
        }
    }
    //Sort by "true" ripeness value, from lowest to highest
    Collections.sort(suggestedFruits, new FruitColorRipenessComparator());
    return suggestedFruits;
}
/**
 * @source https://stackoverflow.com/questions/2839137/how-to-use-comparator-in-java-to-sort
 */
public class FruitColorRipenessComparator implements Comparator<FruitColor> {
    @Override
    public int compare(FruitColor a, FruitColor b){
        return a.getFactorialRipeness() < b.getFactorialRipeness() ? -1 : a.getFactorialRipeness() == b.getFactorialRipeness() ? 0 : 1;
    }
}
 
    