0
protected static double averagePrice( List<ComputerComponent> list ) { 
    return  list.stream()
            //  .filter( line-> EnumUtils.isValidEnum(ComputerComponentCategory.class, line.getCategory()) )    

            //  .filter( line-> isInEnum( line.getCategory(), ComputerComponentCategory.class) )    
            //   .filter( line->  inEnum(line.getCategory(),EnumUtils.getEnumMap(ComputerComponentCategory.class ).keySet() ))  
                .filter(    line -> 
                        line.getCategory().contains("CPU")  
                        || line.getCategory().contains("GPU")
                        || line.getCategory().contains("Monitor")
                        || line.getCategory().contains("Keyboard")
                        || line.getCategory().contains("Mouse")
                        || line.getCategory().contains("Storage")
                        || line.getCategory().contains("Memory")) 
               .mapToDouble(ComputerComponent::getPrice)
               .average() 
               .orElseThrow(NoSuchElementException:: new); 
    }

I have an enum as

public enum ComputerComponentCategory {

    CPU("CPU"), 
    MONITOR("Monitor"), 
    KEYBOARD("Keyboard"), 
    MOUSE("Mouse"), 
    GPU("GPU"), 
    MEMORY("Memory"),
    STORAGE("Storage"),
    NULL("NOT DEFINED"); 

    private String label;

    ComputerComponentCategory(String label) {
        this.label = label;
    }

    public String getLabel() {
        return this.label;
    }

    public static ComputerComponentCategory getValue(String label) {

        switch(label) {
            case "CPU":
                return CPU;
            case "Monitor":
                return MONITOR;
            case "Keyboard":
                return KEYBOARD;
            case "Mouse":
                return MOUSE;
            case "GPU":
                return GPU;
            case "Memory":
                return MEMORY;
            case "Storage":
                return STORAGE;
            default: 
                return NULL ;
        }

    }
}

I pass a list of ComputerComponent class to the averagePrice() function which has two fields of price which is of type double and category which is of type String.

My list has 4 elements with categories as "CPU", "Mouse", "Keyboard" and "Storage" with their respective prices as 34.0, 155.0, 23.0 and 75.0.

When I try to use inEnum(), isInEnum() or EnumUtils.isValidEnum() functions, I get the average price as 34.0 which I think that they just return the price of the first element rather than the average.

But when I do filtering using 

                            .filter(    line -> 
                        line.getCategory().contains("CPU")  
                        || line.getCategory().contains("GPU")
                        || line.getCategory().contains("Monitor")
                        || line.getCategory().contains("Keyboard")
                        || line.getCategory().contains("Mouse")
                        || line.getCategory().contains("Storage")
                        || line.getCategory().contains("Memory"))

I get the correct average value of 71.75.

The implementations that I have used for isInEnum() and inEnum() functions are the following:

public static <E extends Enum<E>> boolean isInEnum(String value, Class<E> enumClass) {
      for (E e : enumClass.getEnumConstants()) {
        if(e.name().contains(value)) { return true; }
      }
      return false;
    }


public static boolean inEnum ( String category, Set<String> value ) {   
    for(String s: value ) {
    if ( category.contains(s) ) {
        return true ;  
    }
    }
    return false ;
}

How can I use enums correctly with java streams to filter by valid category names and get the correct average value of price?

What mistake I am making when using streams and its functions ?

1 Answer 1

1

You could simply use your ComputerCategoryValue.getValue method and check for null, given the category of line:

public class EnumTest {

    @Test
    public void testBothMethods() {
        final ComputerComponent c1 = new ComputerComponent(ComputerComponentCategory.CPU.getLabel(), 12.21);
        final ComputerComponent c2 = new ComputerComponent(ComputerComponentCategory.MEMORY.getLabel(), 23.45);
        final List<ComputerComponent> list = Arrays.asList(c1, c2);

        assertEquals(averagePriceWithFilter(list), averagePriceWithInEnum(list), 0.01);
    }

    protected static double averagePriceWithFilter(final List<ComputerComponent> list) {
        return list.stream()
                .filter(line -> line.getCategory().contains("CPU")
                        || line.getCategory().contains("GPU")
                        || line.getCategory().contains("Monitor")
                        || line.getCategory().contains("Keyboard")
                        || line.getCategory().contains("Mouse")
                        || line.getCategory().contains("Storage")
                        || line.getCategory().contains("Memory"))
                .mapToDouble(ComputerComponent::getPrice)
                .average()
                .orElseThrow(NoSuchElementException::new);
    }

    protected static double averagePriceWithInEnum(final List<ComputerComponent> list) {
        return list.stream()
                .filter(line -> ComputerComponentCategory.getValue(line.getCategory()) != null)
                .mapToDouble(ComputerComponent::getPrice)
                .average()
                .orElseThrow(NoSuchElementException::new);
    }
}

EDIT: explaining your mistakes:

  1. EnumUtils.getEnumMap(ComputerComponentCategory.class).keySet()) returns a map of the enum name (not its label), so that the check will only work for CPU as there name and label are the same.

  2. Same for the other method!

You need to use getLabel() instead of name() or use equalsIgnoreCase instead of contains.

Sign up to request clarification or add additional context in comments.

2 Comments

thanks for the explanation. Do you know why EnumUtils.isValidEnum() doesn't work?
Yes, same explanation applies. name of an enum is basically the string representation of the constant, e.g. "MONITOR". Your category is probably using the label "Monitor", which is obviously different (uppercase matters). You could transform the label toUpperCase, but I would stronly suggest just using the handy method that your enum provides as in my answer. Maybe even better: Don't use the category-label in your ComputerComponent, but simply have the category property be typed as ComputerComponentCategory and work with the enums directly.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.