bercik
bercik

Reputation: 174

Compute average value of items (elements) in FilteredList

Let's assume I have a simple application written in JavaFX.
It has a TableView with two columns, one with elements' names and other with elements' values.

Now I want compute average value of items that are visible in TableView, and update it every time elements are filtered by name.

This is what I already have:

@FXML  
public void filterProducts() {

    filteredProducts = new FilteredList<>(FXCollections.observableArrayList(mainApp.getProductList()), p -> true);

    filterBtn.setOnMouseClicked(event -> {
        filteredProducts.setPredicate(product -> {
            return checkFilteringForNetPrice(product) && checkFilteringForManufacturer(product);
        });
    });


    SortedList<Product> sortedProducts = new SortedList<>(filteredProducts);
    sortedProducts.comparatorProperty().bind(productTable.comparatorProperty());
    productTable.setItems(sortedProducts);

    computeAverageAfterFiltering(sortedProducts);
}

With call for computeAverageAfterFiltering(sortedProducts) method I have been trying to compute average value of sortedProducts elements, but it contains exactly the same number of elements as mainApp.getProductList().
I expected to find only filtered and sorted elements in sortedProducts.
What is wrong?

Upvotes: 0

Views: 129

Answers (1)

James_D
James_D

Reputation: 209553

You're only applying the filter when the button is pressed; however you are invoking the method computeAverageAfterFiltering immediately, i.e. before the filter is applied.

You can maintain the average of the elements in the filtered list with a binding:

DoubleBinding average = Bindings.createDoubleBinding(
    () -> computeAverage(sortedProducts),
    sortedProducts);

The first argument to createDoubleBinding is the function that evaluates the binding, and the second is a list of objects to observe for changes. In this case we just pass the sorted list, so any time the list contents change, the binding is recomputed.

Then you can do things like

Label averageLabel = new Label();
averageLabel.textProperty().bind(average.asString("Average price: %f"));

with

private double computeAverage(List<Product> products) {
    return products.stream()
           // adjust Product::getPrice to give the values you want to average:
           .collect(Collectors.averagingDouble(Product::getPrice));
}

Upvotes: 1

Related Questions