Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Tableview Integer sorting bugged?

I have a tableview and one of my columns consist of Integer values:

  • 1
  • 444 -9

If I sort on this column, I would expect 444 to be the "highest" number and therefor be the first row, but JavaFX believes 9 is higher?

Any tips?

MVCE:

public class TableViewTEST extends Application {

private TableView table = new TableView();
final ObservableList<Person> data = FXCollections.observableArrayList(
        new Person("1", "Smith", "[email protected]"),
        new Person("9", "Johnson", "[email protected]"),
        new Person("444", "Williams", "[email protected]")

);

@Override
public void start(Stage stage) {
    table.getItems().addAll(data);

    Scene scene = new Scene(new Group());

    stage.setTitle(
            "Table View Sample");
    stage.setWidth(
            300);
    stage.setHeight(
            500);

    final Label label = new Label("Address Book");

    label.setFont(
            new Font("Arial", 20));

    TableColumn firstNameCol = new TableColumn("First");
    TableColumn lastNameCol = new TableColumn("Last Name");
    TableColumn emailCol = new TableColumn("Email");

    firstNameCol.setCellValueFactory(
            new PropertyValueFactory<Person, String>("firstName")
    );
    lastNameCol.setCellValueFactory(
            new PropertyValueFactory<Person, String>("lastName")
    );
    emailCol.setCellValueFactory(
            new PropertyValueFactory<Person, String>("email")
    );

    table.getColumns()
            .addAll(firstNameCol, lastNameCol, emailCol);

    final VBox vbox = new VBox();

    vbox.setSpacing(
            5);
    vbox.setPadding(
            new Insets(10, 0, 0, 10));
    vbox.getChildren()
            .addAll(label, table);

    ((Group) scene.getRoot()).getChildren().addAll(vbox);

    stage.setScene(scene);

    stage.show();
}

/**
 * @param args the command line arguments
 */
public static void main(String[] args) {
    launch(args);
}

}

Edit

Okay, I have found out the reason for this is because I use String in the model.. So the updated question is, how can you create a new sorting functionality on the table column so that it sorts as integer even though there's string in it?

like image 540
miniHessel Avatar asked Feb 28 '26 10:02

miniHessel


2 Answers

Your values are Strings, so they are sorted lexicographically; since the character '4' comes before the character '9', '444' comes before '9'.

If you make those fields integer fields, then they will be sorted numerically. It's helpful here to use properly typed TableViews and TableColumns, instead of raw types.

import javafx.application.Application;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.Insets;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.layout.VBox;
import javafx.scene.text.Font;
import javafx.stage.Stage;

public class TableViewSortTest extends Application {

private TableView<Person> table = new TableView<>();
final ObservableList<Person> data = FXCollections.observableArrayList(
        new Person(1, "Smith", "[email protected]"),
        new Person(9, "Johnson", "[email protected]"),
        new Person(444, "Williams", "[email protected]")

);

@Override
public void start(Stage stage) {
    table.getItems().addAll(data);

    Scene scene = new Scene(new Group());

    stage.setTitle(
            "Table View Sample");
    stage.setWidth(
            300);
    stage.setHeight(
            500);

    final Label label = new Label("Address Book");

    label.setFont(
            new Font("Arial", 20));

    TableColumn<Person, Integer> idCol = new TableColumn<>("Id");
    TableColumn<Person, String> lastNameCol = new TableColumn<>("Last Name");
    TableColumn<Person, String> emailCol = new TableColumn<>("Email");

    idCol.setCellValueFactory(
            new PropertyValueFactory<Person, Integer>("id")
    );
    lastNameCol.setCellValueFactory(
            new PropertyValueFactory<Person, String>("name")
    );
    emailCol.setCellValueFactory(
            new PropertyValueFactory<Person, String>("email")
    );

    table.getColumns()
            .addAll(idCol, lastNameCol, emailCol);

    final VBox vbox = new VBox();

    vbox.setSpacing(
            5);
    vbox.setPadding(
            new Insets(10, 0, 0, 10));
    vbox.getChildren()
            .addAll(label, table);

    ((Group) scene.getRoot()).getChildren().addAll(vbox);

    stage.setScene(scene);

    stage.show();
}

public static class Person {
    private final IntegerProperty id = new SimpleIntegerProperty(this, "id");
    private final StringProperty name = new SimpleStringProperty(this, "name");
    private final StringProperty email = new SimpleStringProperty(this, "email");

    public Person(int id, String name, String email) {
        this.id.set(id);
        this.name.set(name);
        this.email.set(email);
    }

    public final IntegerProperty idProperty() {
        return this.id;
    }

    public final int getId() {
        return this.idProperty().get();
    }

    public final void setId(final int id) {
        this.idProperty().set(id);
    }

    public final StringProperty nameProperty() {
        return this.name;
    }

    public final java.lang.String getName() {
        return this.nameProperty().get();
    }

    public final void setName(final java.lang.String name) {
        this.nameProperty().set(name);
    }

    public final StringProperty emailProperty() {
        return this.email;
    }

    public final java.lang.String getEmail() {
        return this.emailProperty().get();
    }

    public final void setEmail(final java.lang.String email) {
        this.emailProperty().set(email);
    }


}

/**
 * @param args the command line arguments
 */
public static void main(String[] args) {
    launch(args);
}

}
like image 101
James_D Avatar answered Mar 04 '26 09:03

James_D


You can create a Comparator:

firstNameCol.setComparator(new CustomComparator());

EDIT

Assuming you have a String type in your column, as it was originally posted, and you want to sort it numerically trying to parse all the strings, and in case there'are some non-integer strings, sort these lexicographically, this will be a valid Comparator:

private class CustomComparator implements Comparator<String>{

    @Override
    public int compare(String o1, String o2) {
        if (o1 == null && o2 == null) return 0;
        if (o1 == null) return -1;
        if (o2 == null) return 1;

        Integer i1=null;
        try{ i1=Integer.valueOf(o1); } catch(NumberFormatException ignored){}
        Integer i2=null;
        try{ i2=Integer.valueOf(o2); } catch(NumberFormatException ignored){}

        if(i1==null && i2==null) return o1.compareTo(o2);
        if(i1==null) return -1;
        if(i2==null) return 1;

        return i1-i2;
    }
}
like image 32
José Pereda Avatar answered Mar 04 '26 10:03

José Pereda