ObservableList javafx sort descending with Comparator - javafx

ObservableArrayList.sorted works well with primitive data types but if instead reference datatypes can not work
ObservableList<NewClass> observableArrayList = FXCollections.observableArrayList();
observableArrayList.add(new NewClass(1, "name 1"));
observableArrayList.add(new NewClass(2, "name 2"));
observableArrayList.add(new NewClass(4, "name 4"));
observableArrayList.add(new NewClass(5, "name 5"));
observableArrayList.add(new NewClass(3, "name 3"));
observableArrayList.sorted(Comparator.reverseOrder());
class NewClass
private int stt
private int name
Error message
incompatible types: inference variable T has incompatible bounds
equality constraints: sf.enforcement.NewClass
upper bounds: java.lang.Comparable<? super T>
I want to sort in the stt order of the Newclass, please help me

Comparator.reverseOrder only works if there is an order to reverse. This is only the case if NewClass implements Comparable. Otherwise you need to use a custom comparator:
observableArrayList.sort((a, b) -> Integer.compare(b.getStt(), a.getStt()));
or
Comparator<NewClass> comparator = Comparator.comparingInt(NewClass::getStt);
observableArrayList.sort(comparator.reversed());

Related

CellCache renders unexpectedly in TableView with tornadoFX

I just puzzled by the cellCache funtion usage in TornadoFX with Kotlin. The render is always strange and unexpected, what happens with that? The code is here:
data class Person(var name : String, var age : Int, var sex : Boolean)
class MyView: View()
{
val list = listOf<Person>(
Person("Name A", 20, false),
Person("Name B", 22, false),
Person("Name C", 21, true),
Person("Name D", 30, true),
Person("Name E", 35, true)
).observable()
override val root = stackpane {
vbox {
tableview(items = list) {
column("Name", Person::name)
column("Age", Person::age)
column("Sex", Person::sex).cellCache{
checkbox{
isSelected = it
setOnAction {
println("Selection: $isSelected")
}
}
}
}
}
}
}
I expect all the rows will render with a checkbox in column "Sex", but the code above will just display two: one selected and another unselected, what the hell is going on with cellcache?
I also find changing the data class with the one below, and it is all right:
data class Person(var name : String, var age : Int, var sex : SimpleBooleanProperty)
However I must change a lot with the data class properties in this way, I also don't know why.
Help me, and thanks in advance!
The cellCache creates a unique cell per item in your list. If your items does not implement meaningful equals and hashCode functions you will run into trouble with cellCache since the same cell will be used for multiple underlying items.
Adding equals and hashCode implementation that ensures each item is unique should solve the problem. It seems that the default implementation in a Kotlin data class doesn't cut it :)
If you can't or won't provide equals and hashCode implementations I would suggest using cellFormat instead, which doesn't rely on object equality to produce a unique cell.
I'm sorry this isn't made clear in the guide, I will update the TableView part with more information and a warning about the use of cellCache.

Java 8 Streams API to filter Map entries

I have a the following container in Java that I need to work on
Map<String, List<Entry<Parameter, String>>>
Where Parameter is an enumerated type defined as follows:
public enum Parameter {
Param1,
Param2,
Param3
}
The code below shows how I initialize the map structure - effectively putting 2 rows in the container.
Map<String, List<Entry<Parameter, String>>> map2 = new HashMap<String, List<Entry<Parameter, String>>>() {{
put("SERVICE1", new ArrayList<Entry<Parameter, String>>(){{
add (new AbstractMap.SimpleEntry<>(Parameter.Param1,"val1"));
add (new AbstractMap.SimpleEntry<>(Parameter.Param2,"val2"));
add (new AbstractMap.SimpleEntry<>(Parameter.Param3,"val3"));
}});
put("SERVICE2", new ArrayList<Entry<Parameter, String>>(){{
add (new AbstractMap.SimpleEntry<>(Parameter.Param1,"val4"));
add (new AbstractMap.SimpleEntry<>(Parameter.Param2,"val5"));
add (new AbstractMap.SimpleEntry<>(Parameter.Param3,"val6"));
}});
}};
I need to use the java 8 streams api to find the val1 and val2 values from "SERVICE1" but I do not know the correct java streams filter and mapping syntax.
The nearest thing I can come up with is the following, but this only filters at the top level and it returns a list of lists rather than the list of Parameter.Param1,"val1" & Parameter.Param2,"val3" that I am looking for from the SERVICE1 row.
List<List<Entry<Parameter, String>>> listOfLists = myMap.entrySet().stream()
.filter(next -> next.getKey().equals("SERVICE1"))
.map(Map.Entry::getValue)
.collect(Collectors.toList());
listOfLists.size();
If you only need the "val1" and "val2" values, you can first use getOrDefault to get the corresponding list, and then filter on the entries' keys to get entries with Param1 or Param2 as key, and finally apply map again to get the values of these entries.
List<String> list =
myMap.getOrDefault("SERVICE1", Collections.emptyList())
.stream()
.filter(e -> e.getKey() == Parameter.Param1 || e.getKey() == Parameter.Param2)
.map(Map.Entry::getValue)
.collect(Collectors.toList());
Also you might be interested to look at Efficiency of Java "Double Brace Initialization"?

SelectMany Expression Tree Expression.Call typeArguments

Well, I have a problem with creating expression tree for SelectMany.. especially at the typeArguments part..
So, I have a database with tables like below:
[Group] (one to many) [GroupDetail] (many to one) [Item] (one to many) [ItemDetail]
GroupDetail.group is a Group
GroupDetail.item is an Item
ItemDetail.item is an Item
Item.itemDetail is a collection of ItemDetail
Group.groupDetail is a collection of GroupDetail
so you can see that group detail is simply a many to many link for Group and Item
and (one to many) is a one to many relation..
For example, the data is like below:
Group, GroupDetail, Item, ItemDetail
------------------------------------
gr1, grDt1, ItemA, PartsAA
gr1, grDt1, ItemA, PartsAB
gr1, grDt2, ItemB, PartsBA
gr1, grDt2, ItemB, PartsBB
gr2, grDt3, ItemC, PartsCA
gr2, grDt4, ItemA, PartsAA
gr2, grDt4, ItemA, PartsAB
gr3, grDt4, ItemD, PartsDA
gr3, grDt5, ItemE, PartsEA
I want to select items and each of it's detail by a group search
and return it as a collection of some sort of view class..
Similar like this function below:
public IQueryable<ItemGroupDetailView> getViewQ(IQueryable<GroupDetail> myQ)
{
return myQ.SelectMany(
m => m.item.itemDetail,
(m, n) => new ItemGroupDetailView
{
groupName = m.group.name,
groupDetailCount = m.group.groupDetail.Count,
item = new ItemView
{
itemName = n.item.name,
itemDetailCount = n.item.itemDetail.Count
},
itemDetail = new ItemDetailView
{
itemDetailName = n.name
}
}
);
}
simply like that above BUT I want it to be a dynamic exp tree instead, so maybe I can just use it like:
Filter filter = new Filter("gr1","ItemA"); // just a filter
var myQ = getSearchQ(filters); // it gets all the where etc, everything is fine here..
var viewQ = getViewQ(myQ); // simply to convert the data to the view,.. where all the errors are
var finalQ = ApplyLimit(ApplyGrouping(ApplySorting(ApplySelect(myQ))); // paging, sorting, grouping, etc..
// run the select.. get the count etc..
now I want to make it dynamic but I seems to get it wrong on the SelectMany part
This is roughly how I do the SelectMany things:
step 1: I bind the property/field assignment.. it came from some sort of list-string-configuration-kinda-thing that map the assignment
PropertyInfo pInfo;
MemberExpression mExp;
// parse getproperty reflect etc...
List<MemberAssignment> memberAssginments = new List<MemberAssignment>();
memberAssginments.Add(Expression.Bind(pInfo, mExp);
step 2: then the usual member init
MemberInitExpression mie =
Expression.MemberInit(Expression.New
(typeof(ItemGroupDetailView)), memberAssginments);
so I get this:
new ItemGroupDetailView
{
groupName = m.group.name,
groupDetailCount = m.group.groupDetail.Count,
item = new ItemView
{
itemName = n.item.name,
itemDetailCount = n.item.itemDetail.Count
},
itemDetail = new ItemDetailView
{
itemDetailName = n.name
}
}
step 3: then get the expression collectionSelector & resultSelector
ParamterExpression m = Expression.Parameter(typeof(GroupDetail),"m");
ParamterExpression n = Expression.Parameter(typeof(ItemDetail),"n");
Expression<Func<GroupDetail, ItemDetail, ItemGroupDetailView>> exp2 =
Expression.Lambda<Func<GroupDetail, ItemDetail, ItemGroupDetailView>>
(mie, new ParameterExpression[] { m, n });
I think I get what I need, exp2 (resultSelector):
(m, n) => new ItemGroupDetailView
{
groupName = m.group.name,
groupDetailCount = m.group.groupDetail.Count,
item = new ItemView
{
itemName = n.item.name,
itemDetailCount = n.item.itemDetail.Count
},
itemDetail = new ItemDetailView
{
itemDetailName = n.name
}
}
and with similar way I get the other clause, exp1 (collectionSelector)
MemberExpression mEx = .... reflect get property/field etc..
Expression<Func<GroupDetail, IEnumerable<ItemDetail>>> exp1 =
Expression.Lambda<Func<GroupDetail, IEnumerable<ItemDetail>>>(mEx, m);
so I get this:
m => m.item.itemDetail
step 4: then get the selectMany MethodCallExpression itself
MethodCallExpression selectManyExp =
Expression.Call(
typeof(Queryable),
"SelectMany",
new Type[] {
typeof(GroupDetail),
typeof(Expression<Func<GroupDetail, IEnumerable<ItemDetail>>>),
typeof(Expression<Func<GroupDetail, ItemDetail, ItemGroupDetailView>>)
},
new Expression[] { Expression.Quote(exp1), Expression.Quote(exp2) }
);
It doesnt work at all..
(No generic method 'SelectMany' on type 'System.Linq.Queryable' is compatible with the supplied type arguments and arguments. No type arguments should be provided if the method is non-generic.)
so I think the main question here is:
How do I build an expression tree for such selectMany query
How do I build an expression query that has resultSelector & collectionSelector and multiple parameters..
and why does the code below works, but the Expression.Call always error..
myQ.SelectMany(exp1, exp2);
I guess I dont understand how SelectMany or Expression Tree works.. :(
but, I need this to be dynamic because the property/field assignment binding and source, selector and result is dynamic
public IQueryable<TView> getViewQ(IQueryable<T> myQ)
{
// some code..
}
EDIT 1 :
Switching the exp1 and exp2.. now exp1 is the collectionSelector and exp2 is the resultSelector..
EDIT 2 :
Furthermore I tried several things:
first, I change the type argument like what Mike said below, but the error still the same
MethodCallExpression selectManyExp =
Expression.Call(
typeof(Queryable),
"SelectMany",
new Type[] {
typeof(GroupDetail),
typeof(ItemDetail),
typeof(ItemGroupDetailView)
},
new Expression[] { Expression.Quote(exp1), Expression.Quote(exp2) }
);
then I try some reflection this and that to check
System.Reflection.MethodInfo sminfo = null;
System.Reflection.MethodInfo sminfo2 = null;
IEnumerable<System.Reflection.MethodInfo> sminfos = typeof(Queryable)
.GetMethods(System.Reflection.BindingFlags.Static
| System.Reflection.BindingFlags.Public)
.Where(xxx => xxx.Name.Equals("SelectMany"));
foreach (System.Reflection.MethodInfo mi in sminfos)
{
if (mi.GetParameters().Count() == 3)
{
sminfo = mi;
}
}
/*
I ran this step by step to make sure that the method I get in sminfo is:
public static IQueryable<TResult> SelectMany<TSource, TCollection, TResult>(
this IQueryable<TSource> source,
Expression<Func<TSource, IEnumerable<TCollection>>> collectionSelector,
Expression<Func<TSource, TCollection, TResult>> resultSelector
);
*/
sminfo2 = sminfo.MakeGenericMethod(
new Type[] {
typeof(GroupDetail), typeof(ItemDetail), typeof(ItemGroupDetailView)
});
MethodCallExpression selectManyExp =
Expression.Call(sminfo2, new Expression[] { exp1, exp2 });
and I get different error:
(Incorrect number of arguments supplied for call to method ..)
and it tells me that the method required 3 parameters instead of 2, and the one I miss is the IQueryable<GroupDetail> source
so I get back to the Expression.Call and add the source parameter
MethodCallExpression selectManyExp =
Expression.Call(
typeof(Queryable),
"SelectMany",
new Type[] {
typeof(GroupDetail),
typeof(ItemDetail),
typeof(ItemGroupDetailView)
},
new Expression[] { myQ.Expression, exp1, exp2 }
);
return (IQueryable<ItemGroupDetailView>)myQ.Provider.CreateQuery(selectManyExp);
and it works.. :D
Sorry for the messy and long post,.. my English is bad.. :(
It looks like you've conflated the type parameters with the formal parameters. I believe your type arguments should look like below:
MethodCallExpression selectManyExp =
Expression.Call(
typeof(Queryable),
"SelectMany",
new Type[] {
typeof(GroupDetail),
typeof(ItemDetail),
typeof(ItemGroupDetailView)
},
new Expression[] { Expression.Quote(exp1), Expression.Quote(exp2) }
);

Using scanner and multi variable arrays

So, I have this tiny problem. I'm prompting the user to input 3 variables (Str, Str, Int) that need to be stored in a multi variable array and I can't get it to work. Any help will be appreciated.
LibraryBook[] book = new LibraryBook[5];
//inputing a new book
Scanner input = new Scanner(System.in);
LibraryBook[] myBook = new LibraryBook[0];
System.out.println("Enter book name: ");
String title = input.nextLine().trim();
System.out.println("Enter author name: ");
String author = input.nextLine().trim();
System.out.println("Enter # pages: ");
int pages = input.nextInt();
myBook[0] =new LibraryBook(title,author,pages);
I get this error
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 0
at LibraryBookSort.main(LibraryBookSort.java:36)
----jGRASP wedge2: exit code for process is 1.
delete this line
LibraryBook[] myBook = new LibraryBook[0];
and replace last line
myBook[0] =new LibraryBook(title,author,pages);
by this
book[0] =new LibraryBook(title,author,pages);
You're initializing your array with zero size, so you basically don't have any space to store a variable (a class in your case). Here's the correction:
LibraryBook[] myBook = new LibraryBook[10];
I'm assuming you will need no more than 10 locations in your program.
Edit: I just noticed you have two LibraryBook arrays declared, but you're only using one. Is there a necessity for the unused one?

Create a VB.NET Array with two columns of values?

I know how to create an array and loop through it normally - but what if I need a multi-column array. e.g. usually I might do something like:
For Each row in NameofArray
Dim name as String = row
Response.Write("Hello " & name & "!")
Next
But what if I want to do something like:
For Each row in NameofArray
Dim name as String = row.name
Dim age as Integer = row.age
Response.Write("Hello " & name & "! You are " & age & " years old!"
Next
If this isn't possible with an array, is there another way I can accomplish this?
Create your custom data type:
public struct DataType
public string Name;
public int Age;
}
Such type you can than use in an array like that:
DataType[] myData = new DataType[100];
myData[0].Name = "myName";
myData[0].Age = 100;
Note, if looping through that array via foreach, the elements returned for each iteration cannot get altered. If this is an requirement for you, consider using 'class' rather than 'struct' in the above DataType declaration. This will come with some other implications though. For example, the instances of a class DataType will explicitely have to be created via the 'new' keyword.
After reading your comment I think my other answer is probably what you are looking for.
What type is row and what type is NameOfArray?
If you would like to make row into a coumpound type with several members then there a several options.
Structure Row
Public Name as String
Public Age as Integer
End Structure
for instance. If you would prefer a reference type substitute Class for Structure.
Or using anonymous types,
Dim row = New With {Name = "Bob", Age = 21}
Then you can use generics to make a list of rows that you can iterate through using ForEach.
Dim NameOfList As System.Collections.Generic.List(of Row)
or if it were a result of a LINQ query somthing that supported
IEnumerable(of New With{Name As String, Age As Int}). //Not sure if this is VB
I'm not certain I uderstand your question and hope this is the kind of thing you were looking for.
As you can see from my fellow answerers, the support for anonymous types is superior in C# but, since you asked the question in VB.Net I will limit myself to that context.
After reading your comment I think I understand the question.
You can do
///Spacer Top
Dim NameOfArray = {New With {.Age = 21, .Name = "Bob"}, New With {.Age = 74, .Name = "Gramps"}}
///Spacer Bottom
If you want to create an IEnumberable anonymous type of Name Age tuples ;-p
Did you tried Dictionary Class. You can loop through the Dictionary using KeyValue pair class.
// Create a new dictionary of strings, with string keys.
//
Dictionary<string, string> openWith =
new Dictionary<string, string>();
// Add some elements to the dictionary. There are no
// duplicate keys, but some of the values are duplicates.
openWith.Add("txt", "notepad.exe");
openWith.Add("bmp", "paint.exe");
openWith.Add("dib", "paint.exe");
openWith.Add("rtf", "wordpad.exe");
foreach(var item in openWith)
{
Console.WriteLine(item.Key +" can be open with " + item.value);
}
You need to (can) index into your array using the two dimensions ie...
Dim array(,) As Object = { _
{"John",26}, _
{"Mark",4} _
}
For row As Integer = 0 to array.GetUpperBound(0)
Dim name as String = CStr(array(row,0))
Dim age as Integer = CInt(array(row,1))
Response.Write("Hello " & name & "! You are " & age & " years old!")
Next
Though would be better storing this sort of information in a class or user defined type of some kind.

Resources