Vaadin 8 - Sorting items in ComboBox<Integer> - java

I have a ComboBox of Type Integer that I setup like this:
Collection<Integer> cbItems = new HashSet<>();
for(Integer i = 100; i < 360; i+=5){
cbItems.add(i);
}
ComboBox<Integer> cb = new ComboBox<>();
cb.setItems(cbItems);
I create a Collection of Integers and fill it with certain Integer values (100, 105, 110, etc...). The Code compiles, and the ComboBox is displayed in the view.
My Problem is that the items in the ComboBox are not sorted (or better: not sorted in the way I thought it would be).
Why does it re-sort my Integer-Collection, and how can I prevent it?

I would recommend you to populate vaadin components like ComboBox with DataProvider. It will make things easier later.
Your own solution works fine if there is not added items later to the ComboBox elsewhere. If items are added it might be needed to perform Collections.sort() again.
With a little change to use DataProvider:
ListDataProvider<Integer> dp = new ListDataProvider<>(cbItems);
// the first param is function that returns the value to sort
// in case of Integer it is that Integer itself.
dp.setSortOrder(i -> {return i;}, SortDirection.ASCENDING);
ComboBox<Integer> combo = new ComboBox<>();
combo.setDataProvider(dp);
Now, if you later add items to combo (through the original Collection):
// add some items to see where they are sorted
cbItems.add(102);
cbItems.add(113);
these items should be sorted to right place in ComboBox.
Then consider a bit more complex example. If you had a class like:
#RequiredArgsConstructor
public class Wrapper {
#Getter
private final Integer id;
#Getter
private final String name;
}
and you wanted to sort it by name descending, it would be like (with test data):
// generate some dymmy data
Collection<Wrapper> wrappers = new HashSet<>();
for(int i=1000; i<=2000; i+=150) {
wrappers.add(new Wrapper(i,
"Rand"+ThreadLocalRandom.current().nextInt(5000, 6000)) );
}
ListDataProvider<Wrapper> dpWrappers = new ListDataProvider<>(wrappers);
// now function returns the name of the wrapper to sort as sort argument
dpWrappers.setSortOrder(wrapper -> {return wrapper.getName();},
SortDirection.DESCENDING);
ComboBox<Wrapper> comboWrappers = new ComboBox<>();
comboWrappers.setDataProvider(dpWrappers);
// remember to set this to have the name of wrapper in combo vaptions
// instead of the value of Wrapper.toString();
comboWrappers.setItemCaptionGenerator( item -> {return item.getName();});

Okay I figured it out:
I changed the Collection to a List (ArrayList) so I can use Collections.sort(cbItems);
List<Integer> cbItems= new ArrayList<>();
for(Integer i = 100; i < 360; i+=5){
cbItems.add(i);
}
Collections.sort(cbItems);
ComboBox<Integer> cb = new ComboBox<>();
cb.setItems(cbItems);
Now the items are sorted ascendingly.

Related

Convert list to set by ImmutableSet.copyOf() and new HashSet<>(list);

Good day,
I have a set that contains some data:
Set< AItem > aItems = aItem
.getItems( );
Because I want to do some sorting, so I convert it to list first, after sort, then only turn it back to set:
List< AItem > tempoAccounts = new ArrayList(aItems);
Collections.sort(tempoAccounts, new Comparator() {
public int compare(Object arg0, Object arg1) {
// sorting logic here
}
});
// convert from list back to set
aItems = ImmutableSet.copyOf(tempoAccounts);
This give me a correct result, all my data sort accordingly.
However, If I want to add more item into aItems:
AItem aai = new AItem();
aai.setAId( (long) 2222 );
aai.setLimit( BigDecimal.ZERO );
then I will hit :
Exception created : [java.lang.UnsupportedOperationException
at com.google.common.collect.ImmutableCollection.add(ImmutableCollection.java:91)
So I change the
aItems = ImmutableSet.copyOf(tempoAccounts);
to
aItems = new HashSet<AItem>(tempoAccounts);
This will not give me UnsupportedOperationException if I add new item inside this Set. But my sorting is gone, the Set is not sort properly.
Any ideas to sort my Set, and then can add more item inside without exception?
Kindly advise.
HashSet should be viewed as an unordered set. If you want a sorted set, just use a TreeSet:
Set<AItem> sortedItems = new TreeSet<>(new Comparator<AItem>() { ... });
sortedItems.addAll(aItems);
(Don't use the raw type of Comparator, btw... try to avoid raw types entirely.)
No need to create a list, no need to create an immutable set...

java 8 stream groupingBy sum of composite variable

I have a class Something which contains an instance variable Anything.
class Anything {
private final int id;
private final int noThings;
public Anything(int id, int noThings) {
this.id = id;
this.noThings = noThings;
}
}
class Something {
private final int parentId;
private final List<Anything> anythings;
private int getParentId() {
return parentId;
}
private List<Anything> getAnythings() {
return anythings;
}
public Something(int parentId, List<Anything> anythings) {
this.parentId = parentId;
this.anythings = anythings;
}
}
Given a list of Somethings
List<Something> mySomethings = Arrays.asList(
new Something(123, Arrays.asList(new Anything(45, 65),
new Anything(568, 15),
new Anything(145, 27))),
new Something(547, Arrays.asList(new Anything(12, 123),
new Anything(678, 76),
new Anything(98, 81))),
new Something(685, Arrays.asList(new Anything(23, 57),
new Anything(324, 67),
new Anything(457, 87))));
I want to sort them such that the Something objects are sorted depending on the total descending sum of the (Anything object) noThings, and then by the descending value of the (Anything object) noThings
123 = 65+15+27 = 107(3rd)
547 = 123+76+81 = 280 (1st)
685 = 57+67+87 = 211 (2nd)
So that I end up with
List<Something> orderedSomethings = Arrays.asList(
new Something(547, Arrays.asList(new Anything(12, 123),
new Anything(98, 81),
new Anything(678, 76))),
new Something(685, Arrays.asList(new Anything(457, 87),
new Anything(324, 67),
new Anything(23, 57))),
new Something(123, Arrays.asList(new Anything(45, 65),
new Anything(145, 27),
new Anything(568, 15))));
I know that I can get the list of Anythings per parent Id
Map<Integer, List<Anythings>> anythings
= mySomethings.stream()
.collect(Collectors.toMap(p->p.getParentId(),
p->p.getAnythings()))
;
But after that I'm a bit stuck.
Unless I'm mistaken, you can not do both sorts in one go. But since they are independent of each other (the sum of the nothings in the Anythings in a Something is independent of their order), this does not matter much. Just sort one after the other.
To sort the Anytings inside the Somethings by their noThings:
mySomethings.stream().map(Something::getAnythings)
.forEach(as -> as.sort(Comparator.comparing(Anything::getNoThings)
.reversed()));
To sort the Somethings by the sum of the noThings of their Anythings:
mySomethings.sort(Comparator.comparing((Something s) -> s.getAnythings().stream()
.mapToInt(Anything::getNoThings).sum())
.reversed());
Note that both those sorts will modify the respective lists in-place.
As pointed out by #Tagir, the second sort will calculate the sum of the Anythings again for each pair of Somethings that are compared in the sort. If the lists are long, this can be very wasteful. Instead, you could first calculate the sums in a map and then just look up the value.
Map<Something, Integer> sumsOfThings = mySomethings.stream()
.collect(Collectors.toMap(s -> s, s -> s.getAnythings().stream()
.mapToInt(Anything::getNoThings).sum()));
mySomethings.sort(Comparator.comparing(sumsOfThings::get).reversed());
The problem of other solutions is that sums are not stored anywhere during sorting, thus when sorting large input, sums will be calculated for every row several times reducing the performance. An alternative solution is to create intermediate pairs of (something, sum), sort by sum, then extract something and forget about sum. Here's how it can be done with Stream API and SimpleImmutableEntry as pair class:
List<Something> orderedSomethings = mySomethings.stream()
.map(smth -> new AbstractMap.SimpleImmutableEntry<>(smth, smth
.getAnythings().stream()
.mapToInt(Anything::getNoThings).sum()))
.sorted(Entry.<Something, Integer>comparingByValue().reversed())
.map(Entry::getKey)
.collect(Collectors.toList());
There's some syntactic sugar available in my free StreamEx library which makes the code a little bit cleaner:
List<Something> orderedSomethings = StreamEx.of(mySomethings)
.mapToEntry(smth -> smth
.getAnythings().stream()
.mapToInt(Anything::getNoThings).sum())
.reverseSorted(Entry.comparingByValue())
.keys().toList();
As for sorting the Anything inside something: other solutions are ok.
In the end I added an extra method to the Something class.
public int getTotalNoThings() {
return anythings.stream().collect(Collectors.summingInt(Anything::getNoThings));
}
then I used this method to sort by total noThings (desc)
somethings = somethings.stream()
.sorted(Comparator.comparing(Something::getTotalNoThings).reversed())
.collect(Collectors.toList());
and then I used the code suggested above (thanks!) to sort by the Anything instance noThings
somethings .stream().map(Something::getAnythings)
.forEach(as -> as.sort(Comparator.comparing(Anything::getNoThings).reversed()));
Thanks again for help.

How to get individual array names as strings for array of arrays

// Facility table attributes to be read in json format
String facilityName[], recApp[], recFacility[], sendApp[],
sendFacility[], enable[], doneness[], retryLimit[],
watchDelay[], retryDelay[], ackTimeout[],
keepConOpen[], sendTimeout[], cleanupDelay[],
host[], port[];
String facilityTableAttrs[][] = new String[][] {
facilityName, recApp, recFacility, sendApp,
sendFacility, enable, doneness, retryLimit,
watchDelay, retryDelay, ackTimeout, keepConOpen,
sendTimeout, cleanupDelay, host, port};
I have array of arrays called facilityTableAttrs declared as above.
I have 2 questions:
1) Is it possible to do the above array declaration in a single step ?
2) I wish to get the individual array names of these 1D array using something like:
for(i = 0; i < facilityTableAttrs.length; i++) {
System.out.println(facilityTableAttrs[i].toString());
}
but it fails. How to get the individual array names as string?
The first list of arrays you declare don't seem to be initialized anywhere.
As such they are null, and invoking toString on any of them will cause a NullPointerException to be thrown, hence "it fails".
By the way, invoking toString on an non-null array would actually print something similar to the Object.toString representation, which is not what you want (Arrays.toString(myArray) is your friend here).
You could initialize each and every single array and populate them optionally, before adding them to the main String[][] but I recommend you don't.
Instead, investigate the collections framework.
What you could use here is a Map<String, List<String>>.
Or better even, a custom object with properties such as List<String> facilityName, List<String> recApp, etc.
Finally, note the variable naming, which is camelBack according to code conventions.
This is not possible with arrays. You need to use map, like so:
Map<String, List<String>> myMap = new HashMap<String, List<String>>();
You need to choose correct data structure for your problem.
Arrays are used only for storing values, thay are not interestd in bounding names to them.
Maps on the other hands are great with bounding names (keys that are unique) to any type of value.
I propose to use a wrapper class:
public class Facility {
private final String name;
private final List<String> values;
public Facility(String name) {
this.name = name;
this.values = new ArrayList<>();
}
public String getName() {
return name;
}
public List<String> getValues() {
return values;
}
}
and then do:
Facility[] facilities = new Facility[] {
new Facility("facility 1"),
new Facility("facility 2"),
new Facility("facility 3"),
new Facility("facility 4"),
};
for(Facility facility : facilities) {
System.out.println(facility.getName());
}
To add a value to a facility you'd do:
Facility facility = facilities.get(0);
facility.getValues().add("bla");
If you need to look up facilities by name, then use a Map instead of an array:
...
// see createLookup method below
Map<String, Facility> facilities = createLookup(
new Facility("facility 1"),
new Facility("facility 2"),
new Facility("facility 3"),
new Facility("facility 4"));
// print names
for(Facility facility : facilities.values()) {
System.out.println(facility.getName());
}
// add a value
Facility facility = facilities.get("facility 3");
facility.getValues().add("bla");
}
private Map<String, Facility> createLookup(Facility.. facilities) {
// use TreeMap to have sorted keys
Map<String, Facility> lookup = new TreeMap<>();
for(Facility facility : facilities) {
lookup.put(facility.getName(), facility);
}
return lookup;
}

Java LIst default and modified list

I get a list from the database ingredients, and I make a new list somelist = ingredinets. I convert some values of somelist with convert(somelist). This is changing both lists. Is there a way to keep the original list ingredients? Probably, I am doing something wrong. Should I get a second list from the database?
List <Ingredient> somelist = new ArrayList<>();
somelist = ingredients;
convert.showImperial(somelist);
public List<Ingredient> showImperial(List<Ingredient> ingredients) {
for (int i = 0; i < ingredients.size(); i++) {
switch (ingredients.get(i).getMeasurement()) {
case GRAMS: {
Double value = ingredients.get(i).getValue();
if (value >= 453.59237) {
value = value / 453.59237;
BigDecimal rounded = new BigDecimal(value);
rounded=rounded.setScale(2, RoundingMode.HALF_DOWN);
ingredients.get(i).setValue((rounded.doubleValue())); //to pound
ingredients.get(i).setMeasurement(POUNDS);
} else {
value = value * 16 / 453.59237; //to oz;
System.out.println(value);
BigDecimal rounded = new BigDecimal(value);
rounded = rounded.setScale(1, RoundingMode.HALF_DOWN);
// System.out.println(rounded);
// System.out.println(rounded.doubleValue());
ingredients.get(i).setValue(rounded.doubleValue());
ingredients.get(i).setMeasurement(OUNCE);
}
break;
}
case ML: {
double value = ingredients.get(i).getValue();
ingredients.get(i).setValue(value / 240);
ingredients.get(i).setMeasurement(CUP); //to cup
break;
}
default: {
break;
}
}
}
return ingredients;
}
This line:
somelist = ingredients;
Is not making a copy of ingredients, it's setting somelist to be the same as ingredients.
You can simply add all the elements from ingredients to somelist:
somelist.addAll(ingredients);
...but you also appear to be modifying those, and a List holds a reference to the elements, not the actual elements. So you'll need to go a step further and copy one level deeper:
for (Ingredient ingr : ingredients) {
somelist.add(new Ingredient(ingr)); // or use whatever constructor is appropriate.
}
What you're doing is setting the second list to be a reference to the first. What you want to do is somehow pass the information from the first to the second. Otherwise when one is changed, so is the other.
If you are using an ArrayList as an implementation in somelist you can just create using this constructor.
List <Ingredient> somelist = new ArrayList<>(ingredients);
Note that this is a copy of List, if you add some elements into ingredients won't be reflected in someList and viceversa.
Example:
List <Ingredient> somelist = new ArrayList<>(ingredients);
ingredients.add(new Ingredient()); // won't be reflected in somelist
But if you mutate elements from the list for example via setters then it will reflected in both list.This is a shallow copy.
List <Ingredient> somelist = new ArrayList<>(ingredients);
Ingredient aux=ingredients.get(0);
aux.setSomeProperty("someProperty");//will be reflected in both list
To make a deep copy you have to add a clone method for your Ingredient class to make a deep copy.

How to dynamically prune Java List when Iterator does not support remove()?

I have the following code:
Widget[] widgetArray = widgetService.getAllWidgets();
List<Widget> widgets = Arrays.asList(widgetArray);
// Prune out any Widgets named "Melvin".
Iterator<Widget> iter = widgets.iterator();
while(iter.hasNext()) {
Widget w = iter.next();
if("Melvin".equals(w.getName()))
iter.remove();
}
When I run this code I get a runtime java.lang.UnsupportedOperationExceptionError with a vague exception message of null that gets thrown on the iter.remove() line. It seems that some Java Iterators don't support the remove method and will throw this exception.
I can't change the widgetService.getAllWidgets() method to return a List<Widget> and am stuck with the Widget[] array return value.
So I ask: what can I do to loop through my widgets array and dynamically prune out ones that are named "Melvin"?
If you can afford it, just make a mutable copy of the list. Replace
List<Widget> widgets = Arrays.asList(widgetArray);
with
List<Widget> widgets = new ArrayList<Widget>(Arrays.asList(widgetArray));
Just defer removal until the iterator is done:
Widget[] widgetArray = widgetService.getAllWidgets();
List<Widget> widgets = Arrays.asList(widgetArray);
// Prune out any Widgets named "Melvin".
List<Widget> toRemove = new ArrayList<Widget>();
Iterator<Widget> iter = widgets.iterator();
while(iter.hasNext()) {
Widget w = iter.next();
if("Melvin".equals(w.getName()))
toRemove.add(w);
}
widgets.removeAll(toRemove);
Alternatively, just build the list from eligible widgets using the inverse logic:
List<Widget> widgets = new ArrayList<Widget>();
// Add all Widgets not named "Melvin"
for (Widget w : widgetService.getAllWidgets()) {
if(!"Melvin".equals(w.getName()))
widgets.add(w);
}
The asList() list is still backed by the array.
You may want to loop through each element of the array and add it to a brand new list. This would take two loops.
Or better yet, compare the string value and then add to the list. This way, you have one loop and a brand new list.
With guava you can get rid of all that code and let a fast, well tested library take care of it for you.
Collection<Widget> noMelvins = Collections2.filter( Arrays.asList(widgetArray), new Predicate<Widget>() {
#Override public boolean apply( final Widget arg) {
return !"Melvin".equals(arg.getName());
}
});

Categories

Resources