Filter a list of strings which contains one or more strings from another list with Java 8 streams

丶灬走出姿态 提交于 2019-12-08 07:14:36

问题


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);
    }

}

回答1:


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());



回答2:


The core of your matching should be like this:

productList.stream().filter(
    product -> searchableWords.stream().allMatch(
        searchWord -> product.getPartDesc().toLowerCase().contains(searchWord)
    )
)


来源:https://stackoverflow.com/questions/54710753/filter-a-list-of-strings-which-contains-one-or-more-strings-from-another-list-wi

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!