Reputation: 13
I want to use the strings imputed in a TextField to filter a list. I am using a KeyReleased Event on the TextField to filter the list on every key. The piece of code below filters the list when I type in a word, but when I press space and start typing another word the list gets empty. I am a bit new to streams. I don't know what I am doing wrong.
private ObservableList<Products_Data> productList;
@FXML
private JFXTextField searchField;
@FXML
private TableView<Products_Data> productTable;
@FXML
void searchKeyReleased(KeyEvent event) {
String searchText = searchField.getText();
List<String> searchableWords = Arrays.asList(searchText.toLowerCase().trim().split("\\s+"));
List<Products_Data> filteredList = searchableWords.stream()
.flatMap(i ->productList.stream()
.filter(j -> j.getPartDesc().toLowerCase().contains(i)))
.collect(Collectors.toList());
ObservableList<Products_Data> productFilteredList = FXCollections.observableArrayList(filteredList);
productTable.setItems(productFilteredList);
}
----------
public class Products_Data {
private final StringProperty partDesc = new (this,"PartDesc",null);
public Products_Data() {}
public final StringProperty getPartDescProperty() {return partDesc;}
public final String getPartDesc(){return partDesc.get();}
public final void setPartDesc(String partDesc) {
getPartDescProperty().set(partDesc);
}
}
Upvotes: 1
Views: 839
Reputation: 66263
The core of your matching should be like this:
productList.stream().filter(
product -> searchableWords.stream().allMatch(
searchWord -> product.getPartDesc().toLowerCase().contains(searchWord)
)
)
Upvotes: 0
Reputation: 298439
I can’t see a fundamental problem in your Stream code. The way, you’ve written it, is not very efficient and it allows elements matching multiple words to occur multiple times in the result list. Perhaps, the UI you’re setting the result on can’t handle this.
I’d create a single filter out of the entered text, which will match if any of the words appears in the element, using a case insensitive matching instead of repeatedly converting every string to lowercase. E.g. with a utility method like this:
static final Pattern SPACE = Pattern.compile("\\s+");
public static <T> Predicate<T> getFilter(Function<? super T, String> f, String words) {
String regex = SPACE.splitAsStream(words)
.map(Pattern::quote).collect(Collectors.joining("|"));
Predicate<String> sp = Pattern.compile(regex, Pattern.CASE_INSENSITIVE).asPredicate();
return t -> sp.test(f.apply(t));
}
which can be used as
List<Products_Data> filteredList = productList.stream()
.filter(getFilter(Products_Data::getPartDesc, searchField.getText()))
.collect(Collectors.toList());
Upvotes: 3