Cómo priorizar / resultados FilteredList rango dentro de un predicado?

Zephyr:

Mi solicitud contiene una TextFieldy una ListView. El TextFieldpermite al usuario introducir los términos de búsqueda que filtrará el contenido de la ListViewmedida que escribe.

El proceso de filtrado coincidirá con varios campos dentro de cada uno DataItemen el ListViewy devolver los resultados si alguno de ellos coinciden.

Lo que yo quiero hacer, sin embargo, es tener esos resultados dan prioridad a los elementos que coinciden un campo en particular sobre los demás.

Por ejemplo, en el MCVE a continuación, tengo dos elementos: Computere Paper. El Computerartículo tiene una keywordde "papel", por lo que la búsqueda de "papel" debería devolver Computercomo resultado.

Sin embargo, ya que también tengo un artículo llamado Paper, la búsqueda debe volver Papera la parte superior de la lista. En el MCVE, sin embargo, los resultados se siguen en orden alfabético:

Captura de pantalla

Pregunta: ¿Cómo hago para asegurar cualquier coincidencia con la DataItem.namese enumeran por encima de partidos a una DataItem.keywords?

EDIT: La introducción de "pap" en el campo de búsqueda también debe volver a "papel" en la parte superior, seguido por los partidos que quedan, ya que el término de búsqueda parcial coincide parcialmente el DataItemnombre.


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;
    }
}
JKostikiadis:

Si no se equivoca sólo tiene que encontrar una manera de ordenar correctamente los resultados filtrados. Para hacerlo simple Voy a utilizar este comparador en lugar de la suya:

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

En el código anterior, comparo dos DataItem. Cada uno tendrá una 'puntuación' que depende de la similitud de sus nombres son de nuestra palabra clave de búsqueda. Por simplicidad digamos que damos 1 punto si el searchKeyapareció en el nombre de nuestro artículo y 2 puntos si el nombre de artículo comienza con el searchKey, por lo que ahora podemos comparar los dos y ordenarlos. Si volvemos la elemento1 -1 serán colocados en primer lugar, si volvemos 1 entonces el elemento2 se colocará primero y volver 0 en caso contrario.

Esto es addSearchFilter()el método que se utiliza en el ejemplo:

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

Por supuesto, el findScore()podría ser más sofisticado si se desea crear un sistema de puntuación más complejos (por ejemplo, la comprobación superior y letras minúsculas, dará más puntos en función de la posición de la palabra clave que se encuentran en el nombre del elemento, etc).

Supongo que te gusta

Origin http://43.154.161.224:23101/article/api/json?id=186223&siteId=1
Recomendado
Clasificación