如何在谓词中对FilteredList结果进行优先级排序/排序? [英] How to prioritize/rank FilteredList results within a predicate?

查看:156
本文介绍了如何在谓词中对FilteredList结果进行优先级排序/排序?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的应用程序包含 TextField ListView TextField 允许用户输入搜索字词,以便在键入时过滤 ListView 的内容。



过滤过程将匹配 ListView 中每个 DataItem 中的多个字段如果其中任何一个匹配,则返回结果。



然而,我想要做的是让这些结果优先考虑与某个特定字段相匹配的项目。



例如,在下面的MCVE中,我有两个项目:计算机论文计算机项目的纸张有关键字,因此搜索纸张应返回计算机因此。



但是,因为我还有一个名为 Paper ,搜索应返回列表顶部的 Paper 。但是,在MCVE中,结果仍然按字母顺序排列:





问题:我如何确保与 DataItem.name 的任何匹配都列在上面与 DataItem匹配。关键字



编辑:在搜索字段中输入pap也应在顶部返回Paper ,其余是匹配,因为部分搜索词部分匹配 DataItem 名称。







MCVE




DataItem.java:

  import java.util.List; 

公共类DataItem {

//实例属性
private final IntegerProperty id = new SimpleIntegerProperty();
private final StringProperty name = new SimpleStringProperty();
private final StringProperty description = new SimpleStringProperty();

//搜索关键字列表
private final ObjectProperty< List< String>> keywords = new SimpleObjectProperty<>();

public DataItem(int id,String name,String description,List< String> keywords){
this.id.set(id);
this.name.set(name);
this.description.set(description);
this.keywords.set(keywords);
}

/ **
*创建一个以空格分隔的所有关键字的字符串;用于以后过滤
* /
public String getKeywordsString(){
StringBuilder sb = new StringBuilder();

for(String keyword:keywords.get()){
sb.append(keyword).append();
}

返回sb.toString();

}

public int getId(){
return id.get();
}

public IntegerProperty idProperty(){
return id;
}

public String getName(){
return name.get();
}

public StringProperty nameProperty(){
return name;
}

public String getDescription(){
return description.get();
}

public StringProperty descriptionProperty(){
return description;
}

public List< String> getKeywords(){
return keywords.get();
}

public ObjectProperty< List< String>> keywordsProperty(){
返回关键字;
}

@Override
public String toString(){
return name.get();
}
}



Main .java:

  import javafx.application.Application; 
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.collections.transformation.FilteredList;
import javafx.collections.transformation.SortedList;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.ListView;
import javafx.scene.control.TextField;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;

公共类Main扩展Application {

// TextField用于过滤ListView
TextField txtSearch = new TextField();

// ListView用于保存我们的DataItems
ListView< DataItem> dataItemListView = new ListView<>();

// DataItems的ObservableList
ObservableList< DataItem> dataItems;

public static void main(String [] args){
launch(args);
}

@Override
public void start(Stage primaryStage){

//简单接口
VBox root = new VBox(10 );
root.setAlignment(Pos.CENTER);
root.setPadding(new Insets(10));

//将搜索字段和ListView添加到布局
root.getChildren()。addAll(txtSearch,dataItemListView);

//构建dataItems List
dataItems = FXCollections.observableArrayList(buildDataItems());

//添加过滤器逻辑
addSearchFilter();

//显示阶段
primaryStage.setScene(new Scene(root));
primaryStage.setTitle(Sample);
primaryStage.show();
}

/ **
*添加功能以在输入搜索词时动态过滤列表
* /
private void addSearchFilter(){

//将dataItems列表包装在筛选列表中,最初显示所有项目,按字母顺序排列
FilteredList< DataItem> filteredList = new FilteredList<>(
dataItems.sorted(Comparator.comparing(DataItem :: getName)));

//每当搜索字段改变时,添加谓词来过滤列表
txtSearch.textProperty()。addListener((observable,oldValue,newValue) - >
filteredList。 setPredicate(dataItem - > {

//清除已经存在的任何选择
dataItemListView.getSelectionModel()。clearSelection();

//如果搜索字段为空,显示所有DataItems
if(newValue == null || newValue.isEmpty()){
return true;
}

//比较DataItem的带有搜索查询的名称和关键字(忽略大小写)
String query = newValue.toLowerCase();

if(dataItem.getName()。toLowerCase()。contains(query)){
// DataItem的名称包含搜索查询
return tru e;
} else {

//否则检查是否有任何搜索词匹配DataItem关键字中的搜索词
//我们按空格分割查询,以便我们可以匹配DataItems多个关键字
String [] searchTerms = query.split();
boolean match = false;
for(String searchTerm:searchTerms){
match = dataItem.getKeywordsString()。toLowerCase()。contains(searchTerm);
}
返回匹配;
}
}));

//将已过滤的列表包装在SortedList中
SortedList< DataItem> sortedList = new SortedList<>(filteredList);

//更新ListView
dataItemListView.setItems(sortedList);
}

/ **
*生成样本产品列表
* /
private List< DataItem> buildDataItems(){

List< DataItem> dataItems = new ArrayList<>();

dataItems.add(新的DataItem(
1,学校用品,学习东西。,
Arrays.asList(钢笔,铅笔,纸,橡皮擦)));
dataItems.add(new DataItem(
2,Computer,Do some something,
Arrays.asList(paper,cpu,keyboard,monitor) )));
dataItems.add(new DataItem(
3,Keyboard,Type things,
Arrays.asList(keys,numpad,input)));
dataItems.add(new DataItem(
4,Printer,Print things。,
Arrays.asList(paper,ink,computer)));
dataItems.add(new DataItem(
5,Paper,Report things。,
Arrays.asList(write,printer,notebook)));

返回dataItems;
}
}


解决方案

如果没错,您只需要找到一种方法来正确排序过滤结果。为了简单起见,我将使用这个比较器而不是你的比较器:

 比较器< DataItem> byName = new Comparator< DataItem>(){
@Override
public int compare(DataItem o1,DataItem o2){
String searchKey = txtSearch.getText()。toLowerCase();
int item1Score = findScore(o1.getName()。toLowerCase(),searchKey);
int item2Score = findScore(o2.getName()。toLowerCase(),searchKey);

if(item1Score> item2Score){
return -1;
}

if(item2Score> item1Score){
return 1;
}

返回0;
}

private int findScore(String itemName,String searchKey){
int sum = 0;
if(itemName.startsWith(searchKey)){
sum + = 2;
}

if(itemName.contains(searchKey)){
sum + = 1;
}
返还金额;
}
};

在上面的代码中,我比较了两个DataItem。每个人都有一个'得分',这取决于他们的名字与我们的搜索关键字的相似程度。为简单起见,我们假设我们在项目名称中出现 searchKey 时给出1分,如果项目名称以 searchKey开头,则给出2分,现在我们可以比较这两个并对它们进行排序。如果我们返回-1,则item1将首先放置,如果我们返回1,则item2将首先放置,否则返回0.



这是 addSearchFilter()我在你的例子中使用的方法:

  private void addSearchFilter(){
FilteredList< DataItem> filteredList = new FilteredList<>(dataItems);

txtSearch.textProperty()。addListener((observable,oldValue,newValue) - > filteredList.setPredicate(dataItem - > {

dataItemListView.getSelectionModel()。clearSelection ();

if(newValue == null || newValue.isEmpty()){
return true;
}

String query = newValue .toLowerCase();

if(dataItem.getName()。toLowerCase()。contains(query)){
return true;
} else {
String [] searchTerms = query.split();
boolean match = false;
for(String searchTerm:searchTerms){
match = dataItem.getKeywordsString()。toLowerCase()。contains (searchTerm);
}
返回匹配;
}
}));

SortedList< DataItem> sortedList = new SortedList<>(filteredList);

Comparator< DataItem> byName = new Comparator< DataItem>(){
@Override
public int compare(DataItem o1,DataItem o2){
String searchKey = txtSearch.getText()。toLowerCase();
int item1Score = findScore(o1.getName()。toLowerCase(),searchKey);
int item2Score = findScore(o2.getName()。toLowerCase(),searchKey);

if(item1Score> item2Score){
return -1;
}

if(item2Score> item1Score){
return 1;
}

返回0;
}

private int findScore(String itemName,String searchKey){
int sum = 0;
if(itemName.startsWith(searchKey)){
sum + = 2;
}

if(itemName.contains(searchKey)){
sum + = 1;
}
返还金额;
}
};

sortedList.setComparator(byName);

dataItemListView.setItems(sortedList);
}

当然 findScore()可能会更复杂。


My application contains a TextField and a ListView. The TextField allows users to enter search terms that will filter the contents of the ListView as they type.

The filtering process will match several fields within each DataItem in the ListView and return the results if any of them match.

What I want to do, however, is have those results prioritize items that match one particular field over the others.

For example, in the MCVE below, I have two items: Computer and Paper. The Computer item has a keyword for "paper," so searching for "paper" should return Computer as a result.

However, since I also have an item called Paper, the search should return Paper at the top of the list. In the MCVE, though, the results are still alphabetized:

Question: How would I go about ensuring any matches to the DataItem.name are listed above matches to a DataItem.keywords?

EDIT: Entering "pap" in the search field should also return "Paper" at the top, followed by the remaining matches, as the partial search term partially matches the DataItem name.


MCVE


DataItem.java:

import java.util.List;

public class DataItem {

    // Instance properties
    private final IntegerProperty id = new SimpleIntegerProperty();
    private final StringProperty name = new SimpleStringProperty();
    private final StringProperty description = new SimpleStringProperty();

    // List of search keywords
    private final ObjectProperty<List<String>> keywords = new SimpleObjectProperty<>();

    public DataItem(int id, String name, String description, List<String> keywords) {
        this.id.set(id);
        this.name.set(name);
        this.description.set(description);
        this.keywords.set(keywords);
    }

    /**
     * Creates a space-separated String of all the keywords; used for filtering later
     */
    public String getKeywordsString() {
        StringBuilder sb = new StringBuilder();

        for (String keyword : keywords.get()) {
            sb.append(keyword).append(" ");
        }

        return sb.toString();

    }

    public int getId() {
        return id.get();
    }

    public IntegerProperty idProperty() {
        return id;
    }

    public String getName() {
        return name.get();
    }

    public StringProperty nameProperty() {
        return name;
    }

    public String getDescription() {
        return description.get();
    }

    public StringProperty descriptionProperty() {
        return description;
    }

    public List<String> getKeywords() {
        return keywords.get();
    }

    public ObjectProperty<List<String>> keywordsProperty() {
        return keywords;
    }

    @Override
    public String toString() {
        return name.get();
    }
}


Main.java:

import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.collections.transformation.FilteredList;
import javafx.collections.transformation.SortedList;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.ListView;
import javafx.scene.control.TextField;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;

public class Main extends Application {

    // TextField used for filtering the ListView
    TextField txtSearch = new TextField();

    // ListView to hold our DataItems
    ListView<DataItem> dataItemListView = new ListView<>();

    // The ObservableList of DataItems
    ObservableList<DataItem> dataItems;

    public static void main(String[] args) {
        launch(args);
    }

    @Override
    public void start(Stage primaryStage) {

        // Simple Interface
        VBox root = new VBox(10);
        root.setAlignment(Pos.CENTER);
        root.setPadding(new Insets(10));

        // Add the search field and ListView to the layout
        root.getChildren().addAll(txtSearch, dataItemListView);

        // Build the dataItems List
        dataItems = FXCollections.observableArrayList(buildDataItems());

        // Add the filter logic
        addSearchFilter();

        // Show the stage
        primaryStage.setScene(new Scene(root));
        primaryStage.setTitle("Sample");
        primaryStage.show();
    }

    /**
     * Adds the functionality to filter the list dynamically as search terms are entered
     */
    private void addSearchFilter() {

        // Wrap the dataItems list in a filtered list, initially showing all items, alphabetically
        FilteredList<DataItem> filteredList = new FilteredList<>(
                dataItems.sorted(Comparator.comparing(DataItem::getName)));

        // Add the predicate to filter the list whenever the search field changes
        txtSearch.textProperty().addListener((observable, oldValue, newValue) ->
                filteredList.setPredicate(dataItem -> {

                    // Clear any selection already present
                    dataItemListView.getSelectionModel().clearSelection();

                    // If the search field is empty, show all DataItems
                    if (newValue == null || newValue.isEmpty()) {
                        return true;
                    }

                    // Compare the DataItem's name and keywords with the search query (ignoring case)
                    String query = newValue.toLowerCase();

                    if (dataItem.getName().toLowerCase().contains(query)) {
                        // DataItem's name contains the search query
                        return true;
                    } else {

                        // Otherwise check if any of the search terms match those in the DataItem's keywords
                        // We split the query by space so we can match DataItems with multiple keywords
                        String[] searchTerms = query.split(" ");
                        boolean match = false;
                        for (String searchTerm : searchTerms) {
                            match = dataItem.getKeywordsString().toLowerCase().contains(searchTerm);
                        }
                        return match;
                    }
                }));

        // Wrap the filtered list in a SortedList
        SortedList<DataItem> sortedList = new SortedList<>(filteredList);

        // Update the ListView
        dataItemListView.setItems(sortedList);
    }

    /**
     * Generates a list of sample products
     */
    private List<DataItem> buildDataItems() {

        List<DataItem> dataItems = new ArrayList<>();

        dataItems.add(new DataItem(
                1, "School Supplies", "Learn things.",
                Arrays.asList("pens", "pencils", "paper", "eraser")));
        dataItems.add(new DataItem(
                2, "Computer", "Do some things",
                Arrays.asList("paper", "cpu", "keyboard", "monitor")));
        dataItems.add(new DataItem(
                3, "Keyboard", "Type things",
                Arrays.asList("keys", "numpad", "input")));
        dataItems.add(new DataItem(
                4, "Printer", "Print things.",
                Arrays.asList("paper", "ink", "computer")));
        dataItems.add(new DataItem(
                5, "Paper", "Report things.",
                Arrays.asList("write", "printer", "notebook")));

        return dataItems;
    }
}

解决方案

If not mistaken you only need to find a way to sort your filtered results correctly. To keep it simple I will use this comparator instead of yours :

Comparator<DataItem> byName = new Comparator<DataItem>() {
            @Override
            public int compare(DataItem o1, DataItem o2) {
                String searchKey = txtSearch.getText().toLowerCase();
                int item1Score = findScore(o1.getName().toLowerCase(), searchKey);
                int item2Score = findScore(o2.getName().toLowerCase(), searchKey);

                if (item1Score > item2Score) {
                    return -1;
                }

                if (item2Score > item1Score) {
                    return 1;
                }

                return 0;
            }

            private int findScore(String itemName, String searchKey) {
                int sum = 0;
                if (itemName.startsWith(searchKey)) {
                    sum += 2;
                }

                if (itemName.contains(searchKey)) {
                    sum += 1;
                }
                return sum;
            }
        };

In the code above, I compare two DataItem. Each one will have a 'score' which depends on how similar their names are from our search keyword. For simplicity lets say we give 1 point if the searchKey appeared in the name of our item and 2 points if the item name starts with the searchKey, so now we can compare those two and sort them. If we return -1 the item1 will be placed first, if we return 1 then the item2 will be placed first and return 0 otherwise.

Here is addSearchFilter() method I used in your example :

private void addSearchFilter() {
        FilteredList<DataItem> filteredList = new FilteredList<>(dataItems);

        txtSearch.textProperty().addListener((observable, oldValue, newValue) -> filteredList.setPredicate(dataItem -> {

            dataItemListView.getSelectionModel().clearSelection();

            if (newValue == null || newValue.isEmpty()) {
                return true;
            }

            String query = newValue.toLowerCase();

            if (dataItem.getName().toLowerCase().contains(query)) {
                return true;
            } else {
                String[] searchTerms = query.split(" ");
                boolean match = false;
                for (String searchTerm : searchTerms) {
                    match = dataItem.getKeywordsString().toLowerCase().contains(searchTerm);
                }
                return match;
            }
        }));

        SortedList<DataItem> sortedList = new SortedList<>(filteredList);

        Comparator<DataItem> byName = new Comparator<DataItem>() {
            @Override
            public int compare(DataItem o1, DataItem o2) {
                String searchKey = txtSearch.getText().toLowerCase();
                int item1Score = findScore(o1.getName().toLowerCase(), searchKey);
                int item2Score = findScore(o2.getName().toLowerCase(), searchKey);

                if (item1Score > item2Score) {
                    return -1;
                }

                if (item2Score > item1Score) {
                    return 1;
                }

                return 0;
            }

            private int findScore(String itemName, String searchKey) {
                int sum = 0;
                if (itemName.startsWith(searchKey)) {
                    sum += 2;
                }

                if (itemName.contains(searchKey)) {
                    sum += 1;
                }
                return sum;
            }
        };

        sortedList.setComparator(byName);

        dataItemListView.setItems(sortedList);
    }

Of course the findScore() could be more sophisticated if you want to create a more complex score system (for example checking upper and lower case letters, give more points depending the position of the keyword found in the item name etc).

这篇关于如何在谓词中对FilteredList结果进行优先级排序/排序?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆