Home > Software engineering >  How do I find the average of filtered elements from list in Java
How do I find the average of filtered elements from list in Java

Time:02-23

I wish to find the average of sales by a particular branch in a given year. There is a list which has all the branches and all the years of sale. I've tried to use IntStream to filter the elements with the required year and branch but I'm unable to find the average, There is an error showing in indexOfSaleInTheYear.size(), and I am not sure if the average part code is correct. Please help. Thank You.

Scanner avgSale = new Scanner(System.in);
    System.out.println("Please enter the year for average sale: \n");
    int yearEntered = avgSale.nextInt();

    Scanner branchNameForAvg = new Scanner(System.in);
    System.out.println("Please enter the branch name whose average sale is to be found, case sensitive: \n");
    String branchNameEntered = branchNameForAvg.nextLine();

    Stream<Integer> indexOfSalesInTheYear = IntStream.of(0, Sale.allValues.size()).boxed()
            .filter(i -> Sale.allYears.get(i).equals(yearEntered))
            .filter(i -> Sale.allBranches.get(i).equals(branchNameEntered));
    
    int sum = 0;
    for(int j = 0; j < indexOfSalesInTheYear.size(); j  ){
        sum  = j;
    }
    double avgSaleFinal = sum/indexOfSalesInTheYear.size();

    System.out.println("The average sale for the year "   yearEntered   " is "   avgSaleFinal   ".");
    }

CodePudding user response:

In your existing code, you are never iterating the allValues list. Since you are using streams you can do this.

double average = IntStream.of(0, Sale.allValues.size()-1) // go up to the last index only
                .filter(i -> Sale.allYears.get(i).equals(yearEntered))
                .filter(i -> Sale.allBranches.get(i).equals(branchNameEntered))
                .mapToDouble(i -> Sale.allValues.get(i)) // now you have every sale value for yearEntered and branchNameEntered
                .average().orElse(-1); // or if not present, throw error, or get the optional, etc

Really you shouldn't have separate lists, you should have a list of Sale objects.

CodePudding user response:

Here is how I would approach it. Instead of separate lists of values, years, and branches, have a list of objects that contain these fields:

record Sale(int value, int year, String branch) {
}

Here I am using a record but if you prefer, need to have mutable Sale instances, or are using an earlier version of Java, you could adapt this code to use classes with ordinary getters and setters.

List<Sale> sales = List.of(
    new Sale(12, 2020, "east"),
    new Sale(15, 2020, "west"),
    new Sale(13, 2020, "east"),
    new Sale(16, 2020, "west"),
    new Sale(14, 2020, "east"),
    new Sale(17, 2020, "west"),
    new Sale(18, 2021, "east"),
    new Sale(19, 2021, "west"));

double averageOfSales(int year, String branchName) {
    OptionalDouble average = sales.stream()
        .filter(sale -> sale.year() == year)
        .filter(sale -> sale.branch().equals(branchName))
        .mapToInt(sale -> sale.value())
        .average();
    if (average.isPresent()) {
        return average.getAsDouble();
    }
    throw new NoSuchElementException("no matching year and branch");
}

You may want to simply return the OptionalDouble or throw a different exception; it all depends on how you want to handle the case where there are no values for the year and branch name provided. You may even want to just return 0.0 or NaN or -1.0 or some other sentinel value in that case.

There is no need to sum the values yourself or divide by the number of values to find the mean since IntStream already has methods for finding the sum or average.

Keeping the different pieces of data broken out in separate, parallel arrays is a common pattern among new Java programmers but it really goes against the concept of object oriented programming or even just abstract data types. You don't need to keep arrays of simple values; you can invent your own types that encompass the value, year, branch, month, house number, post code, and whatever else you want to model as part of the domain of the data you're working with.

  • Related