Custom treetableview order - javafx

I have a treetableview which multiple columns. I like to order only by one column so I apply:
treetbvItems.getSortOrder().add("categoria");
This order data by column "categoria" alphabetical but I want to apply my custom order.
For example, if category can be onew of this values: animals, computers, shoes, vehicles... with above sentence, I get tree order by this way:
animals
computers
shoes
vehicles
but if I want (can be any other custom orther):
computers
shoes
animals
vehicles
Is possible or not to do whith JavaFX?

I assume you really mean
TreeTableColumn<String> categoria ;
// ...
treebvItems.getSortOrder().add(categoria);
since the code you posted won't compile.
You can control the ordering for a particular column by setting a Comparator on the column.
The Comparator defines a compareTo(String, String) method that returns an negative int if the first argument comes before the second, a positive int if the second argument comes before the first, and 0 if they are equal.
So you could do something like:
categoria.setComparator((cat1, cat2) -> {
if (cat1.equals(cat2)) {
return 0 ;
}
if ("computers".equals(cat1)) {
return -1 ;
}
if ("computers".equals(cat2)) {
return 1 ;
}
if ("shoes".equals(cat1)) {
return -1 ;
}
if ("shoes".equals(cat2)) {
return 1 ;
}
if ("animals".equals(cat1)) {
return -1 ;
}
if ("animals".equals(cat2)) {
return 1 ;
}
throw new IllegalArgumentException("Unknown categories: "+cat1+", "+cat2);
}
Note that if you have a fixed set of categories, you should probably use an Enum instead of a String. The "natural" order of an Enum is defined by its declaration order.

Related

Stream handling in kotlin

How will Kotlin work the following code?
Will a collection of 5000000 integers be created as temporary collection or will the filter feed its result immediately to the forEach which means, that only 20 integers will be looked at?
If not, how would I be able to avoid the intermediate collection?
Code:
class Tests {
#Test
fun test() {
var counter = 0
(1..10_000_000).filter { it % 2 == 1 }.forEach {
counter++
if (counter > 10)
return
}
}
}
Your code sample uses operations on Iterable<T>, which works eagerly: the .filter { ... } call will process the whole range and produce a list storing intermediate results.
To alter that, consider using Sequence<T> (e.g. with .asSequence()) that works lazily, so that the intermediate operations such as .filter { ... } produce another lazy sequence and only do the work when items are queried by terminal operations such as .forEach { ... }:
(1..10000000).asSequence()
.filter { it % 2 == 1 }
.forEach { /* ... */ }
See: Kotlin's Iterable and Sequence look exactly same. Why are two types required?
You can actually see the answer to your question pretty quickly by just adding println(it) into filter:
//...
.filter {
println(it)
it % 2 == 1
}
//...
You'll see every number printed. That's because your processing works eagerly as explained here.
As already suggested, lazy Sequences to the rescue: (1..10_000_000).asSequence()
Now, the println(it) in filter will only print the numbers 1..21, which is definetly preferable in your example.

Java8 - get by index but something similar to 'getOrDefault' for Map?

Is there a cleaner way to check whether a value is present at a particular index like list.getOrDefault(index, "defaultValue"). Or even do a default operation when the particular index is out of range of the list.
The normal way to do this is to check for size of the list before attempting this operation.
The default List interface does not have this functionality. There is Iterables.get in Guava:
Iterables.get(iterable, position, defaultValue);
Returns the element at the specified position in iterable or
defaultValue if iterable contains fewer than position + 1 elements.
Throws IndexOutOfBoundsException if position is negative.
If this is functionality you intend to use a lot and can't afford to depend on third-party libraries, you could write your own static method (here inspired by the Guava Lists class):
public class Lists {
public static <E> E getOrDefault(int index, E defaultValue, List<E> list) {
if (index < 0) {
throw new IllegalArgumentException("index is less than 0: " + index);
}
return index <= list.size() - 1 ? list.get(index) : defaultValue;
}
}

How to filter empty groups from a reduction?

It appears to me that Crossfilter never excludes a group from the results of a reduction, even if the applied filters have excluded all the rows in that group. Groups that have had all of their rows filtered out simply return an aggregate value of 0 (or whatever reduceInitial returns).
The problem with this is that it makes it impossible to distinguish between groups that contain no rows and groups that do contain rows but just legitimately aggregate to a value of 0. Basically, there's no way (that I can see) to distinguish between a null value and a 0 aggregation.
Does anybody know of a built-in Crossfilter technique for achieving this? I did come up with a way to do this with my own custom reduceInitial/reduceAdd/reduceRemove method but it wasn't totally straight forward and it seemed to me that this is behavior that might/should be more native to Crossfilter's filtering semantics. So I'm wondering if there's a canonical way to achieve this.
I'll post my technique as an answer if it turns out that there is no built-in way to do this.
A simple way to accomplish this is to have both count and total be reduce attributes:
var dimGroup = dim.group().reduce(reduceAdd, reduceRemove, reduceInitial);
function reduceAdd(p, v) {
++p.count;
p.total += v.value;
return p;
}
function reduceRemove(p, v) {
--p.count;
p.total -= v.value;
return p;
}
function reduceInitial() {
return {count: 0, total: 0};
}
Empty groups will have zero counts, so retrieving only non-empty groups is easy:
dimGroup.top(Infinity).filter(function(d) { return d.value.count > 0; });
OK, there doesn't seem to be any obvious answer jumping out so I'll answer my own question and post the technique I used to solve this.
This example assumes that I've already created a dimension and grouping, which is passed in as groupDim. Because I want to be able to sum up any arbitrary numeric field, I also pass in fieldName so that it will be available in the closure scope of my the reduction functions.
One important characteristic of this technique is that it relies on there being a way to uniquely identify which group each row belongs to. Thinking in term of OLAP, this is essentially the "tuple" that defines a particular aggregation context. But it can be anything you want as long as it deterministically returns the same value for all data rows belonging to a given group.
The end result is that empty groups will have an aggregate value of "null" which can be easily detected for and filtered out after the fact. Any group with at least one row will have a numeric value (even if it happens to be zero).
Refinements or suggestions to this are more then welcome. Here's the code with comments inline:
function configureAggregateSum(groupDim, fieldName) {
function getGroupKey(datum) {
// Given datum return key corresponding to the group to which the datum belongs
}
// This object will keep track of the number of times each group had reduceAdd
// versus reduceRemove called. It is used to revert the running aggregate value
// back to "null" if the count hits zero. This is unfortunately necessary because
// Crossfilter filters as it is aggregating so reduceAdd can be called even if, in
// the end, all records in a group end up being filtered out.
//
var groupCount = {};
function reduceAdd(p, v) {
// Here's the code that keeps track of the invocation count per group
var groupKey = getGroupKey(v);
if (groupCount[groupKey] === undefined) { groupCount[groupKey] = 0; }
groupCount[groupKey]++;
// And here's the implementation of the add reduction (sum in my case)
// Note the check for null (our initial value)
var value = +v[fieldName];
return p === null ? value : p + value;
}
function reduceRemove(p, v) {
// This code keeps track of invocations of invocation count per group and, importantly,
// reverts value back to "null" if it hits 0 for the group. Essentially, if we detect
// that group has no records again we revert to the initial value.
var groupKey = getGroupKey(v);
groupCount[groupKey]--;
if (groupCount[groupKey] === 0) {
return null;
}
// And here's the code for the remove reduction (sum in my case)
var value = +v[fieldName];
return p - value;
}
function reduceInitial() {
return null;
}
// Once returned, can invoke all() or top() to get the values, which can then be filtered
// using a native Array.filter to remove the groups with null value.
return groupedDim.reduce(reduceAdd, reduceRemove, reduceInitial);
}

Merge sorted lists into new list

So i need to write a program that merges two sorted lists into a new third sorted list and I can ONLY use operations of the ADT sorted list.
As with all my other assignments I start with pseudocode but I am in a funk tonight and can't wrap my head around some pseudocode for this.
I have written some pointers though: Create a new sorted list; while the two lists are not empty remove and compare them, to add one or the other to the new list. Stop when the lists are empty (but do think about what happens when one becomes empty before the other!).
Any and all help is very appreciated
EDIT: To let you know I am just looking for Pseudocode help NOT actual code
function merge (lista, listb) {
toReturn = your 'third list'
while lista and listb both still have elements {
if lista's smallest element < listb's smallest element {
add lista's smallest element to toReturn
remove lista's smallest element
} else {
add listb's smallest element to toReturn
remove listb's smallest element
}
}
// if lista has no elements, this loop is skipped
while lista still has elements {
add them to toReturn
}
// if listb has no elements, this loop is skipped
while listb still has elements {
add them to toReturn
}
return toReturn
}
this can be done by using merge procedure of merge sort
this is my cde where i have passed all the 3 list from main function
public static void merge(int A[],int A1[],int B1[],int n1,int n2)
{
int a[]=new int[n1+1];
int b[]=new int[n2+1];
for(int k=0;k<n1;k++){
a[k]=A1[k];
}
for(int k=0;k<n2;k++){
b[k]=B1[k];
}
int i=0,j=0;
a[n1]=Integer.MAX_VALUE;
b[n2]=Integer.MAX_VALUE;
for(int k=0;k<A.length;k++){
if(a[i]>b[j]){
A[k]=b[j];
j++;
}
else{
A[k]=a[i];
i++;
}
}
}

Sorting in AdvancedDatagrid in Flex 3

I am using AdvancedDatagrid in Flex 3. One column of AdvancedDatagrid contains numbers and alphabets. When I sort this column, numbers come before alphabets (Default behavior of internal sorting of AdvancedDatagrid). But I want alphabets to come before number when I sort.
I know I will have to write the custom sort function. But can anybody give some idea on how to proceed.
Thanks in advance.
Use sortCompareFunction
The AdvancedDataGrid control uses this function to sort the elements of the data provider collection. The function signature of the callback function takes two parameters and has the following form:
mySortCompareFunction(obj1:Object, obj2:Object):int
obj1 — A data element to compare.
obj2 — Another data element to compare with obj1.
The function should return a value based on the comparison of the objects:
-1 if obj1 should appear before obj2 in ascending order.
0 if obj1 = obj2.
1 if obj1 should appear after obj2 in ascending order.
<mx:AdvancedDataGridColumn sortCompareFunction="mySort"
dataField="colData"/>
Try the following sort compare function.
public function mySort(obj1:Object, obj2:Object):int
{
var s1:String = obj1.colData;
var s2:String = obj2.colData;
var result:Number = s1.localeCompare(s2);
if(result != 0)
result = result > 0 ? 1 : -1;
if(s1.match(/^\d/))
{
if(s2.match(/^\d/))
return result;
else
return 1;
}
else if(s2.match(/^\d/))
return -1;
else
return result;
}
It checks the first character of strings and pushes the ones that start with a digit downwards in the sort order. It uses localeCompare to compare two strings if they both start with letters or digits - otherwise it says the one starting with a letter should come before the one with digit. Thus abc will precede 123 but a12 will still come before abc.
If you want a totally different sort where letters always precede numbers irrespective of their position in the string, you would have to write one from the scratch - String::charCodeAt might be a good place to start.

Resources