Javafx: Re-sorting a column in a TableView - tableview

I have a TableView associated to a TreeView. Each time a node in the TreeView is selected, the TableView is refreshed with different data.
I am able to sort any column in the TableView, just pressing the corresponding column header. That works fine.
But: when I select a different node in the tree-view, eventhough the column headers keep showing as sorted. The data is not.
Is there a way to programmatically enforce the sort order made by the user each time the data changes?

Ok, I found how to do it. I will summarize it here in case it is useful to others:
Before you update the contents of the TableView, you must save the sortcolum (if any) and the sortType:
TableView rooms;
...
TableColumn sortcolumn = null;
SortType st = null;
if (rooms.getSortOrder().size()>0) {
sortcolumn = (TableColumn) rooms.getSortOrder().get(0);
st = sortcolumn.getSortType();
}
Then, after you are done updating the data in the TableView, you must restore the lost sort-column state and perform a sort.
if (sortcolumn!=null) {
rooms.getSortOrder().add(sortcolumn);
sortcolumn.setSortType(st);
sortcolumn.setSortable(true); // This performs a sort
}
I do not take into account the possibility of having multiple columns in the sort, but this would be very simple to do with this information.

I had the same problem and found out that after an update of the data you only have to call the function sort() on the table view:
TableView rooms;
...
// Update data of rooms
...
rooms.sort()
The table view knows the columns for sorting thus the sort function will sort the new data in the wanted order.
This function is only available in Java 8.

If your TableView is not reinitialized, you can also do the following:
TableColumn<BundleRow, ?> sortOrder = rooms.getSortOrder().get(0);
rooms.getSortOrder().clear();
rooms.getSortOrder().add(sortOrder);

The example of fornacif works, but not if there is more than one sort order (try shift-click on a second column to create secondary sort order).
To do a re-sort on all columns you would need to do something like this:
List<TableColumn<Room, ?>> sortOrder = new ArrayList<>(roomTable.getSortOrder());
roomTable.getSortOrder().clear();
roomTable.getSortOrder().addAll(sortOrder);

If you use the TableView.setItems() method, it appears to reset several aspects of the TableView. Leave the ObservableList in the TableView in place, clear its contents, and then add your new items. Then, TableView.sort() will still know which columns were previously sorted and it will work. Like this:
tableView.getItems().clear();
tableView.getItems().addAll(newTableData);
tableView.sort();

Marco Jakob's answer is good for most cases, but I found that I needed to create a comparator that matches the table sort order for more flexibility. You can then use any method that takes a comparator to do sorting, searching, etc. To create the comparator, I extended that ComparatorChain class from apache's Common-Collections to easily do multiple column sorting. It looks like this.
public class TableColumnListComparator extends ComparatorChain {
public TableColumnListComparator(ObservableList<? extends TableColumn> columns) {
// Get list of comparators from column list.
for (TableColumn column : columns) {
addComparator(new ColumnComparator(column));
}
}
/**
* Compares two items in a table column as if they were being sorted in the TableView.
*/
private static class ColumnComparator implements Comparator {
private final TableColumn column;
/**
* Default Constructor. Creates comparator based off given table column sort order.
*
* #param column
*/
public ColumnComparator(TableColumn column) {
this.column = column;
}
#Override
public int compare(Object o1, Object o2) {
// Could not find a way to do this without casts unfortunately
// Get the value of the column using the column's cell value factory.
final ObservableValue<?> obj1 = (ObservableValue) column.getCellValueFactory().call(
new TableColumn.CellDataFeatures(column.getTableView(), column, o1));
final ObservableValue<?> obj2 = (ObservableValue) column.getCellValueFactory().call(
new TableColumn.CellDataFeatures(column.getTableView(), column, o2));
// Compare the column values using the column's given comparator.
final int compare = column.getComparator().compare(obj1.getValue(), obj2.getValue());
// Sort by proper ascending or descending.
return column.getSortType() == TableColumn.SortType.ASCENDING ? compare : -compare;
}
}
}
You can then sort at anytime with
Collections.sort(backingList, new TalbeColumnListComparator(table.getSortOrder());
I use this to sort multiple lists with the same sort, sort on background threads, do efficient updates without resorting the whole list, etc. I think there are going to be some improvements to table sorting in Javafx 8 so this won't be necessary in the future.

You can also use a SortedList.
SortedList<MatchTableBean> tableItems = new SortedList<>(
observableList, Comparator.comparing(MatchTableBean::isMarker).reversed().thenComparing(MatchTableBean::getQueryRT));
tableItems.comparatorProperty().bind(table.comparatorProperty());
table.setItems(tableItems);
This way the table is sorted, even when the content changes or is completely replaced.

You can also do this for 0 or more Sort-Columns:
List<TableColumn<Room, ?>> sortColumns = new LinkedList<>(rooms.getSortOrder());
// rooms.setItems(...)
rooms.getSortOrder().addAll(sortColumns);
The reason why you create a new LinkedList is that you don't wanna just point at rooms.getSortOrder() like this:
List<TableColumn<Room, ?>> sortColumns = rooms.getSortOrder();
because this way both rooms.getSortOrder() and sortColumns will become empty after you call rooms.setItems(...) which seems to clear the rooms.getSortOrder().

Related

Sitecore Sortorder vs Subitem Sorting

In Sitecore, I thought that sortorder always took precedence over subitem sorting. I was under the assumption that it doesn't matter what an item's subitem sorting was set to if one of it's children had a sortorder of 100 and the other had one of 0 the one with 0 would show up first. On a recent project I'm seeing the opposite happen in the content tree. Subitem sorting is being given the priority. Is there a way to configure the order in which sortorder and subitem sorting is checked? I've been looking around my web.config and diffed it with one I had for a project that was working the way I thought it should but I couldn't find anything that jumped out to me.
Visual of what I'm seeing in the content tree for the project that seems to give subitem sorting priority:
parent - subitem sorting = created
child1 - created = 01012014, sortorder = 100
child2 - created = 02022014, sortorder = 0
This may be a bug in Sitecore but it also may be working as intended, you may have to contact Sitecore support to find out which.
The Child Sorting for "Created" points to Sitecore.Data.Comparers.CreatedComparer,Sitecore.Kernel. If you look at the ExtractKey() method in this class you will see that it does not include the original item's SortOrder. Without out this Sitecore will only sort on Created Date.
public override IKey ExtractKey(Item item)
{
Assert.ArgumentNotNull(item, "item");
KeyObj keyObj = new KeyObj()
{
Item = item,
Key = this.GetCreationDate(item)
};
return keyObj;
}
In contrast you can look at the Child Sorting for "Updated" which points to Sitecore.Data.Comparers.UpdatedComparer,Sitecore.Kernel. In its ExtractKey() method you see it is returning the item's Sort order so you will get a blended sort between Updated Date and Sort Order.
public override IKey ExtractKey(Item item)
{
Assert.ArgumentNotNull(item, "item");
KeyObj keyObj = new KeyObj()
{
Item = item,
Key = item.Statistics.Updated,
Sortorder = item.Appearance.Sortorder
};
return keyObj;
}
When you specify a custom sorter to be used, it's entirely up to the code that runs the comparer on how items are sorted.
The comparer must be a System.Collections.Generic.IComparer, but after that there's no guarantee on whether the sort order field will be used, or that if it is used it will be in an ascending or descending order.
I recommend examining the sorter that is selected for the particular item, and using dotPeek to analyze the source code for the sorter.
When building customized sorters, it's good practice to extend Sitecore's Sitecore.Data.Comparers.Comparer class and override the DoCompare method. It will consistently handle the sort order field, and fall back to the DoCompare method for secondary sorting.

Sorting a NotesDocumentCollection based on a date field in SSJS

Using Server side javascript, I need to sort a NotesDcumentCollection based on a field in the collection containing a date when the documents was created or any built in field when the documents was created.
It would be nice if the function could take a sort option parameter so I could put in if I want the result back in ascending or descending order.
the reason I need this is because I use database.getModifiedDocuments() which returns an unsorted notesdocumentcollection. I need to return the documents in descending order.
The following code is a modified snippet from openNTF which returns the collection in ascending order.
function sortColByDateItem(dc:NotesDocumentCollection, iName:String) {
try{
var rl:java.util.Vector = new java.util.Vector();
var tm:java.util.TreeMap = new java.util.TreeMap();
var doc:NotesNotesDocument = dc.getFirstDocument();
while (doc != null) {
tm.put(doc.getItemValueDateTimeArray(iName)[0].toJavaDate(), doc);
doc = dc.getNextDocument(doc);
}
var tCol:java.util.Collection = tm.values();
var tIt:java.util.Iterator = tCol.iterator();
while (tIt.hasNext()) {
rl.add(tIt.next());
}
return rl;
}catch(e){
}
}
When you construct the TreeMap, pass a Comparator to the constructor. This allows you to define custom sorting instead of "natural" sorting, which by default sorts ascending. Alternatively, you can call descendingMap against the TreeMap to return a clone in reverse order.
This is a very expensive methodology if you are dealing with large number of documents. I mostly use NotesViewEntrycollection (always sorted according to the source view) or view navigator.
For large databases, you may use a view, sorted according to the modified date and navigate through entries of that view until the most recent date your code has been executed (which you have to save it somewhere).
For smaller operations, Tim's method is great!

Rearranging active record elements in Yii

I am using a CDbCriteria with its own conditions, with & order clauses. However, the order i want to give to the elements in the array is way too complex to specify in the order clause.
The solution i have in mind consists of obtaining the active records with the defined criteria like this
$theModelsINeed = MyModel::model()->findAll($criteria);
and then rearrange the order from my php code. How can i do this? I mean, i know how to iterate through its elements, but i donĀ“t know if it is possible to actually change them.
I have been looking into this link about populating active records, but it seems quite complicated and maybe someone could have some better advice.
Thanks
There is nothing special about Yii's active records. The find family of methods will return an array of objects, and you can sort this array like any other array in PHP.
If you have complex sort criteria, this means that probably the best tool for this is usort. Since you will be dealing with objects, your user-defined comparison functions will look something like this:
function compare($x, $y)
{
// First sort criterion: $obj->Name
if ($x->Name != $y->Name) {
return $x->Name < $y->Name ? -1 : 1; // this is an ascending sort
}
// Second sort criterion: $obj->Age
if ($x->Age != $y->Age) {
return $x->Age < $y->Age ? 1 : -1; // this is a descending sort
}
// Add more criteria here
return 0; // if we get this far, the items are equal
}
If you do want to get an array as a result, you can use this method for fetching data that supports dbCriteria:
$model = MyModel::model()->myScope();
$model->dbCriteria->condition .= " AND date BETWEEN :d1 AND :d2";
$model->dbCriteria->order = 'field1 ASC, field2 DESC';
$model->dbCriteria->params = array(':d1'=>$d1, ':d2'=>$d2);
$theModelsINeed = $model->getCommandBuilder()
->createFindCommand($model->tableSchema, $model->dbCriteria)
->queryAll();
The above example shows using a defined scope and modifying the condition with named parameters.
If you don't need Active Record, you could also look into Query Builder, but the above method has worked pretty well for me when I want to use AR but need an array for my result.

Does HBase scan returns sorted columns?

I am working on a HBase map reduce job and need to understand if the columns in a single column family are returned sorted by their names (key). If so, I wouldnt need to do it in the shuffle sort stage.
Thanks
I have a very similar data model as you. Upon insertion however, I set my own values for the timestamps on the Put object. However, I did so in a way that took a "seed" of the current time and appended a incrementing counter for each event I persisted in the batch.
When I pulled the results out from the Scan, I wrote a comparator:
public class KVTimestampComparator implements Comparator<KeyValue> {
#Override
public int compare(KeyValue kv1, KeyValue kv2) {
Long kv1Timestamp = kv1.getTimestamp();
Long kv2Timestamp = kv2.getTimestamp();
return kv1Timestamp.compareTo(kv2Timestamp);
}
}
Then sorted the raw row:
List<KeyValue> row = Arrays.asList(result.raw());
Collections.sort(row, new KVTimestampComparator());
Got this idea from person who answered this : Sorted results from hbase scanner
no, columns are not sorted
They are stored internally as key-value pairs in a long byte array. But, you should clarify your question about what you actually need this for.

Can you sort Typed DataSet DataTables with Linq OrderBy?

I have a Typed DataSet DataTable which inherits TypedTableBase<T>, which in turn implements IEnumerable<T>. I can't seem to get this to work.
myDataTable.OrderBy(x => x.ID).ThenBy(y => y.ID2);
Instead I have to assign this statement to an IEnumerable(or List), then refill my DataTable manually with the newly ordered IEnumerable before I commit. Is this how it is intended to be? I've thought about creating my own extension method that will empty/refill my DataTables, but would this be wise?
Note: Typically I only need to sort for viewing purposes using DataView. But in this case I have a custom routine that must create a new access database with sorting requirements, which means I need to sort the actual DataTable so that I may re-commit it.
Thank you.
In order to do what you want, you must add the following reference to your project:
System.Data.DataSetExtensions
Once you have that added, you can order your DataTable like this:
var query = myDataTable.OrderBy(x => x.ID).ThenBy(y => y.ID2);
// use the DataView generated from the LINQ query
DataView dtv = query.AsDataView();
In order to iterate through the DataView, you can do the following:
var dtv = query.AsDataView();
foreach(DataRowView rw in dtv)
{
// you can also cast back to the typed data...
MyCustomRowType typedRow = (MyCustomRowType) rw.Row;
// do something here...
}
Alternatively you can typecast via LINQ this way:
var dtv = query.AsDataView().Cast<MyCustomRowType>();
// rowItem is of type MyCustomRowType...
foreach(var rowItem in dtv)
{
// do something here...
}
Linq extension methods do not alter the source enumerable.
var numbers = new int[]{1,2,3};
var reversed = numbers.OrderByDescending(x=>x);
foreach(var number in reversed)
Console.Write(number); // 321
foreach(var number in numbers)
Console.Write(number); // 123
If you want to sort a DataTable, you should be using DataViews. You create a view on your DataTable, then apply a Sort or Filter to it, then bind against it. Keep in mind DataSets are an older technology and not quite up to date on the latest and the greatest. A "newer" approach would be to use the Entity Framework.

Resources