How to retrieve the first key from Gee.SortedMap in Vala? - vala

How do I retrieve the first key from Gee.SortedMap in Vala?
For example if I have
Gee.SortedMap<int, string> foo = new Gee.TreeMap<int, string> ();
I want to get the first key, i.e. the lowest int in foo.
In Java we have java.util.SortedMap.firstKey(). I cannot find an equivalent in Vala.

The SortedMap has an ascending_keys property that returns a SortedSet. Then you can get the first() item from the SortedSet:
void main () {
Gee.SortedMap<int, string> foo = new Gee.TreeMap<int, string> ();
foo.set(2, "two");
foo.set(1, "one");
foo.set(0, "zero");
print(#"First sorted key: $(foo.ascending_keys.first())\n");
}

Related

How to change the final type after reduction of a downstream collector in a Java 8 stream?

I got a legacy application using data structures like those in the following toy snippet and I can't easily change these data structures.
I use a Java 8 (only) stream to do some stats and I failed to get the wished type using Collectors.
package myIssueWithCollector;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.BinaryOperator;
import java.util.stream.Collectors;
public class MyIssueWithCollector {
public static Double latitude(Map<String, String> map) {
String latitude = map.get("LATITUDE");
return Double.valueOf(latitude);
}
private static int latitudeComparator(double d1, double d2) {
// get around the fact that NaN > +Infinity in Double.compare()
if (Double.isNaN(d1) && !Double.isNaN(d2)) {
return -1;
}
if (!Double.isNaN(d1) && Double.isNaN(d2)) {
return 1;
}
return Double.compare(Math.abs(d1), Math.abs(d2));
}
public static Map<String, String> createMap(String city, String country, String continent, String latitude) {
Map<String, String> map = new HashMap<>();
map.put("CITY", city);
map.put("COUNTRY", country);
map.put("CONTINENT", continent);
map.put("LATITUDE", latitude);
return map;
}
public static void main(String[] args) {
// Cities with dummies latitudes
// I can not change easily these legacy data structures
Map<String, String> map1 = createMap("London", "UK", "Europa", "48.1");
Map<String, String> map2 = createMap("New York", "USA", "America", "42.4");
Map<String, String> map3 = createMap("Miami", "USA", "America", "39.1");
Map<String, String> map4 = createMap("Glasgow", "UK", "Europa", "49.2");
Map<String, String> map5 = createMap("Camelot", "UK", "Europa", "NaN");
List<Map<String, String>> maps = new ArrayList<>(4);
maps.add(map1);
maps.add(map2);
maps.add(map3);
maps.add(map4);
maps.add(map5);
//////////////////////////////////////////////////////////////////
// My issue starts here:
//////////////////////////////////////////////////////////////////
Map<String, Map<String, Double>> result = maps.stream()
.collect(Collectors.groupingBy(m -> m.get("CONTINENT"),
Collectors.groupingBy(m -> m.get("COUNTRY"), Collectors.reducing(Double.NaN, m -> latitude(m),
BinaryOperator.maxBy((d1, d2) -> latitudeComparator(d1, d2))))));
System.out.println(result);
}
}
I need the result type to be
Map<String, Map<String, String>> instead of Map<String, Map<String, Double>>
by converting back "LATITUDE" from Double to String (using a custom format, not Double.toString() ).
I failed to achieve this with Collectors methods like andThen or collectingAndThen,...
I am currently stuck with Java 8.
Is there a way to get a Map<String, Map<String, String>> result using the same stream ?
Instead of using Collectors.reducing(…) with BinaryOperator.maxBy(…) you can also use Collectors.maxBy. Since this collector doesn’t support an identity value, it requires a finisher function to extract the value from an Optional, but your task requires a finisher anyway, to format the value.
Map<String, Map<String,String>> result = maps.stream()
.collect(Collectors.groupingBy(m -> m.get("CONTINENT"),
Collectors.groupingBy(m -> m.get("COUNTRY"),
Collectors.mapping(MyIssueWithCollector::latitude,
Collectors.collectingAndThen(
Collectors.maxBy(MyIssueWithCollector::latitudeComparator),
o -> format(o.get()))))));
This assumes format to be your custom format function like
private static String format(double d) {
return String.format("%.2f", d);
}
But sometimes, it might be worthwhile to implement your own collector instead of combining multiple built-in collectors.
Map<String, Map<String,String>> result = maps.stream()
.collect(Collectors.groupingBy(m -> m.get("CONTINENT"),
Collectors.groupingBy(m -> m.get("COUNTRY"),
Collector.of(
() -> new double[]{Double.NEGATIVE_INFINITY},
(a, m) -> {
double d = latitude(m);
if(!Double.isNaN(d)) a[0] = Double.max(a[0], d);
},
(a, b) -> a[0] >= b[0]? a: b,
a -> format(a[0])))));
A collector maintains its state using a mutable container, this custom collector uses an array of length one to be able to hold a double value (which eliminates the need to box it to Double objects). Instead of implementing a special comparator to treat NaN specially, it uses a conditional, to never let NaN get into the array in the first place. That’s why the combiner doesn’t need to care about NaN; it can simply return the larger of the two values.
The finisher function just invokes the custom format function with the double value.
You can use Collectors.collectingAndThen to convert the reduced double value to a corresponding String:
Map<String, Map<String, String>> result = maps.stream().collect(
Collectors.groupingBy(
m -> m.get("CONTINENT"),
Collectors.groupingBy(
m -> m.get("COUNTRY"),
Collectors.collectingAndThen(
Collectors.reducing(
Double.NaN,
m -> latitude(m),
BinaryOperator.maxBy(
(d1, d2) -> latitudeComparator(d1, d2)
)
),
MyIssueWithCollector::myToString
)
)
)
);
Here, myToString is some method in the MyIssueWithCollector class to return String from double with your custom format, for example,
public static String myToString(double d) {
return "[latitude=" + d + "]";
}
Using Collectors reducing, you can maintain the latitude's String type in the identity so that the downstream collector is returning a String.
Map < String, Map < String, String >> result = maps.stream()
.collect(
Collectors.groupingBy(m - > m.get("CONTINENT"),
Collectors.groupingBy(m - > m.get("COUNTRY"),
Collectors.reducing("NaN", m - > m.get("LATITUDE"),
BinaryOperator.maxBy((s1, s2) - > latitudeComparator(Double.valueOf(s1), Double.valueOf(s2)))))));

How to create a Grid without POJO (dynamic columns)?

The question was asked here: https://vaadin.com/forum/thread/18095407/how-to-create-a-grid-without-binder
However the vaadin's forum closed so i want to continue it here.
On Vaadin 14, Any recommendation on the best way to implement grid with dynamic varying number of columns. Using column Index (1,2,3...) is not a good choice for me. Let say I have a simple Json file (only 1 level: key-value) to map to a grid and this Json has an unknown list of properties.
which approach is better in term of performance ?:
[Option 1]
class Data {
private Map<String, Object> values = new HashMap<>();
public void set(String key, Object val) {
values.put(key, val);
}
public Object get(String key) {
return values.get(key);
}
}
Grid<Data> myGrid = new Grid<>();
[Option 2]
public class GridDynamicValueProvider implements ValueProvider<GridDynamicRow, Object> {
private int columnIndex;
public GridDynamicValueProvider(int columnIndex) {
this.columnIndex = columnIndex;
}
#Override
public Object apply(GridDynamicRow dynamicRow) {
return dynamicRow.getValue(columnIndex);
}
}
public class GridDynamicRow {
private List<Object> values = new ArrayList<>();
public void addValue(String value) {
values.add(value);
}
public Object getValue(int columnIndex) {
return values.get(columnIndex);
}
}
The SerializablePredicate of Vaadin accepts both function references and Lambdas, thus it is possible to use non-POJO data types with Grid and Binder in Vaadin, although that is a bit unconventional. The key ingredients are:
Grid<Map<String, Integer>> grid = new Grid<>();
...
grid.addColumn(map -> map.get("column")).setHeader("Column");
You can also wrap the Map in custom class if you have need to protect the internals.
class Data {
private Map<String, Object> values = new HashMap<>();
public void set(String key, Object val) {
values.put(key, val);
}
public Object get(String key) {
return values.get(key);
}
}
Grid<Data> myGrid = new Grid<>();
As for the performance, essentially, you're comparing between using a List where you fetch by index versus a HashMap where you fetch by key. Here's a related question: ArrayList .get faster than HashMap .get?
You can use also ArrayList as Grid's type if you can index the columns with a number.
The both approaches allow generating Grid's with varying dynamic number of columns, for example if you read the data directly from a file or have raw data backend queries.

Dart HashMap initial value

I have a final HashMap in a class, How can I have a default value for it?
class RoomsState {
final HashMap<int, int> myMap;
RoomsState({
this.myMap= const {}
});
}
as const {} is a Map and not HashMap I cannot do it, Also HashMap is not a const constructor
Not sure if this is the only way to do it, but it is an option:
import 'dart:collection';
class RoomsState {
final HashMap<int, int> myMap;
RoomsState({
HashMap<int, int>? myMap
}) : this.myMap = myMap ?? HashMap();
}
The myMap parameter are here nullable since we are using the null value to identify if we got any argument.
You could expect a Map in your constructor and then convert it to a HashMap in an initializer.
class RoomsState {
final HashMap<int, int> myMap;
RoomsState({
Map<int, int> map = const {},
}) : myMap = HashMap.from(map);
}
HashMap has the construtor of that creates a HashMap that contains all key/value pairs of other. Check this for reference.
Example:
HashMap<String, String> englishToFrench = HashMap.of({
"go": "aller",
"buy": "acheter",
"sleep": "dormir",
});
Usage example:
void main() {
print(englishToFrench["go"]);
}
Output:
aller

Cast json in to Map<String,Map<String,String>> Format

I need to convert the json string to nested Map and access the same.
Following Json String is in form of Map given below
Map<String,Map<String,String>>
{"0":{"1551874690005":"2","1551874722124":"2","1551874810817":"2","1551874681110":"2","1551874739821":"2","1551874763604":"2","1551874692381":"2","1551874816028":"2","1551874708292":"2","1551874804308":"2","1551874694205":"2","1551874696644":"2","1551874729332":"2","1551874749950":"2","1551874767786":"2"},"1":{"1551948649643":"0","1551948733576":"0","1551948601167":"0","1551948592816":"0","1551948699297":"0","1551874822043":"2","1551948681513":"0","1551948531568":"0","1551948577374":"0","1551948719758":"0","1552370125650":"0","1551948549863":"0","1551948564519":"0","1551948631000":"0","1551953956716":"0"},"2":{"1551875011432":"0","1551875020618":"0","1551874991952":"0","1551875091300":"0","1551875073622":"0","1551875032851":"0","1551874827691":"0","1551948658122":"0","1551874846523":"0","null":"0","1552545417127":"0","1551875083856":"0","1551874929076":"0","1552545972738":"0"},"3":{"1552651031695":"0"},"4":{"1551875144268":"0","1551875157028":"0","1551875115211":"0","1551875124660":"0"}}
Getting following error while trying using my code:
Unhandled Exception: type '_InternalLinkedHashMap<String, dynamic>' is not a subtype of type 'Map<String, String>' in type cast
Map offlineExeStatus = jsonDecode(prefs.getString("offlineExeStatus"));
Map<String,Map<String,String>> exeStatusFinalJson = new Map();
exeStatusFinalJson = offlineExeStatus.cast<String,Map<String,String>();
Need to cast the given json in "exeStatusFinalJson" Map and access like:
exeStatusFinalJson["0"] should give the output like:
{"1551874690005":"2","1551874722124":"2","1551874810817":"2","1551874681110":"2","1551874739821":"2","1551874763604":"2","1551874692381":"2","1551874816028":"2","1551874708292":"2","1551874804308":"2","1551874694205":"2","1551874696644":"2","1551874729332":"2","1551874749950":"2","1551874767786":"2"}
Try to declare your map as Map<String, dynamic> rather than Map<String, Map<String, String>>.
Finally figured out the way to do it!
Map offlineExeStatus = jsonDecode(prefs.getString("onlineExeStatus"));
Map<String, dynamic> exeStatusJson = new Map();
Map<String, Map<String, String>> exeStatusFinalJson = new Map();
List<String> mapKeyExe = new List();
mapKeyExe = offlineExeStatus.keys.cast<String>().toList();
exeStatusJson = offlineExeStatus.cast<String, dynamic>();
for (int i = 0; i < mapKeyExe.length; i++) {
Map<String, String> exeStatusInsideJson = new Map();
exeStatusInsideJson = offlineExeStatus[mapKeyExe[i].toString()]
.cast<String, String>();
exeStatusFinalJson[i.toString()] = exeStatusInsideJson;
print(exeStatusFinalJson);
}

Show distinct values from IndexedContainers in comboboxes in a table in editing mode

In short: The comboboxes in each field of my table in editing mode is giving me a conversion error when selecting an item, but the same logic and containers are working perfectly well outside of the TableFieldFactory (createField()). What am I doing wrong?
Longer explanation:
I have a container with multiple properties (columns) and items (rows). When I edit the table that is connected to this container, I want comboboxes on some of the column fields. I'm using a TableFieldFactory for that, and it is working like a charm.
I want the combobox in each field to contain the distinct elements from its respective property. My solution to this was to implement a method in my Container class that iterate through all properties in the container and for each property creates a new IndexedContainer with unique values from that property. The method returns a map with PropertyIds/Containers, so that I, in createField(), can pick each container from each property I want to have comboboxes for.
Example
So, say I have three propertyId's, Foo, Bar and Baz which each "contains" several items of which some are the same, like so:
Foo
Chris
Chris
Meg
Meg
Meg
Stewie
Stewie
... and the same for Bar and Baz, only other values...
The getDistinctContainers() method returns a Map, looking like this:
Key: PropertyId: Foo
Value: Container: contains propertyId [Foo] and the unique values of Foo, ie. [Chris, Meg, Stewie]
Key: PropertyId: Bar
Value: ... and so forth...
When I am about to set the container datasource in createField(), the container looks like this (for property Foo):
allItemIds: [0, 1, 2]
items: {2={Foo=Stewie}, 1={Foo=Meg}, 0={Foo=Chris}}
propertyIds: [Foo]
... which seems alright to me...
Now, the table shows the comboboxes in each field as intended. But when I click an item in a combobox, it gives me the following conversion error:
com.vaadin.data.util.converter.Converter$ConversionException: Unable to convert value of type java.lang.Integer to model type class java.lang.String. No converter is set and the types are not compatible.
at com.vaadin.data.util.converter.ConverterUtil.convertToModel(ConverterUtil.java:181)
at com.vaadin.ui.AbstractField.convertToModel(AbstractField.java:745)
Note:
I tried creating the same scenario outside of the table, and it worked just fine. So it seems that the comboboxes, with the same logic and the same containers, works fine outside the TableFieldFactory and the createFields() method. I can't put my finger on why they shouldn't work in a TableFieldFactory...
Question:
What do I do to get the comboboxes to set the correct values?
Here's my Container class:
public class SomeContainer extends IndexedContainer {
private static final long serialVersionUID = 1L;
public void addContainerProperties() {
addContainerProperty("Foo", String.class, null);
addContainerProperty("Bar", String.class, null);
addContainerProperty("Baz", String.class, null);
}
public Map<String, Container> getDistinctContainers() {
Map<String, Container> m = new HashMap<String, Container>();
ArrayList<Object> filter = new ArrayList<Object>();
int id = 0;
for (Object propertyId : this.getContainerPropertyIds()) {
Container cont = new IndexedContainer();
cont.addContainerProperty(propertyId, propertyId.getClass(), null);
for (Object itemId : this.getItemIds()) {
Object ob = ((Item) this.getItem(itemId)).getItemProperty(propertyId).getValue();
if ((!filter.contains(ob.toString())) && (ob != null)) {
filter.add(ob.toString());
Item item = cont.addItem(id);
item.getItemProperty(propertyId).setValue(ob);
id++;
}
}
m.put(propertyId.toString(), cont);
}
return m;
}
}
... and here is the relevant code for createField:
#Override
public Field<?> createField(Container container, Object itemId, Object propertyId, com.vaadin.ui.Component uiContext) {
TextField tField = (TextField) DefaultFieldFactory.get().createField(container, itemId, propertyId, uiContext);
tField.setImmediate(true);
// ...some code here that uses the TextField
if (propertyId.equals("Foo")) {
ComboBox select = new ComboBox();
for (Map.Entry<String, Container> entry : distinctContainers.entrySet()) {
if (entry.getKey().equals(propertyId)) {
select.setContainerDataSource(entry.getValue());
}
}
select.setImmediate(true);
select.setItemCaptionPropertyId(propertyId);
select.setItemCaptionMode(AbstractSelect.ITEM_CAPTION_MODE_PROPERTY);
return select;
}
// ... if statements for Bar and Baz left out for brevity...
return tField;
}
Please help me understand what I'm missing!
Thanks in advance!
From the above exception and code snippets we can see that a conversion between Integer (presentation type) and String (model) is required. In this particular case:
presentation type: ItemId = {0,1,2}
model: value of PropertyId = {"Chris", "Meg", "Stewie"}
Since Vaadin has no no built-in IntegerToStringConverter you would need a custom converter:
...
select.setContainerDataSource(entry.getValue());
select.setConverter(new Converter<Object, String>() {
#Override
public String convertToModel(Object itemId, Class<? extends String> paramClass, Locale paramLocale) throws ConversionException {
if (itemId != null) {
IndexedContainer c = (IndexedContainer) entry.getValue();
Object propertyId = c.getContainerPropertyIds().iterator().next();
Object name = c.getItem(itemId).getItemProperty(propertyId).getValue();
return (String) name;
}
return null;
}
#Override
public Object convertToPresentation(String value, Class<? extends Object> paramClass, Locale paramLocale) throws ConversionException {
if (value != null) {
IndexedContainer c = (IndexedContainer) entry.getValue();
Object propertyId = c.getContainerPropertyIds().iterator().next();
for (Object itemId : container.getItemIds()) {
Object name = c.getContainerProperty(itemId, propertyId).getValue();
if (value.equals(name)) {
return itemId;
}
}
}
return null;
}
#Override
public Class<String> getModelType() {
return String.class;
}
#Override
public Class<Object> getPresentationType() {
return Object.class;
}
});
Please notice that is not possible to use explicit Integer<-->String conversion
select.setConverter(new Converter<Integer, String>());
as compiler rejects it. The problem has been described here.
More about Vaadin's converters can be found at:
Book of Vaadin, Chapter 9.2.3: Converting Between Property Type and Representation
Changing the default converters for an application
Creating your own converter for String - MyType conversion
I hope it helps.

Resources