if I have some text in a String like:
"abc=123,def=456,ghi=789"
how could I create a populated HashMap<String,Int> object for it in the easiest, shortest amount of code possible in Kotlin?
I can think of no solution easier than this:
val s = "abc=123,def=456,ghi=789"
val map = s.split(",").associate {
val (left, right) = it.split("=")
left to right.toInt()
}
Or, if you need exactly a HashMap, use .associateTo(HashMap()) { ... }.
Some details:
.associate { ... } receives a function that produces pairs which are then stored into a map as keys and values respectively.
val (left, right) = it.split("=") is the usage of destructuring declarations on the list returned from it.split("="), it takes the first two items from the list.
left to right.toInt() creates a Pair<String, Int> defining a single mapping.
You can map each key/value to a Pair with the to keyword. An iterable of Pair can be mapped to a Map easily with the toMap() extension method.
val s = "abc=123,def=456,ghi=789"
val output = s.split(",")
.map { it.split("=") }
.map { it.first() to it.last().toInt() }
.toMap()
Related
I am trying to create a custom collector in order to count valid elements of a list. I have done it using one of the already provide collectors:
arr.stream()
.filter(e -> e.matches("[:;][~-]?[)D]"))
.map(e -> 1)
.reduce(0, Integer::sum);
but as a challenge for myself, I wanted to create my own custom collector in order to understand it better. And this is where I got stuck.
It is probably something trivial but I am learning this and can't figure a supplier, an accumulator, and a combiner. I guess I still don't understand something about them. For instance, I have a similar stream:
arr1.stream()
.filter(e -> e.matches("[:;][~-]?[)D]"))
.map(e -> 1)
.collect(temporary array, adding to array, reduce);
AFAIK supplier is a function without arguments, which returns something. I studied standard examples and it is usually a method reference for a new collection, e.g. ArrayList::new. I tried to use constant 0, e -> 0 because I want to return a scalar. I think it is wrong because it makes the stream returning 0. If using method reference for a temporary collection, Java complains about a mismatch of types of a supplier and returning object. I am also confused about using an accumulator if the final result is a number as a combiner would reduce all elements to a number, e.g. (a,b) -> a + b`.
I'm completely stumped.
Probably part of your problem is that you cannot obviously create an accumulator for an Integer type since it is immutable.
You start with this:
System.out.println(IntStream.of(1,2,3).reduce(0, Integer::sum));
You can extend to this:
System.out.println(IntStream.of(1,2,3).boxed()
.collect(Collectors.reducing(0, (i1,i2)->i1+i2)));
Or even this, which has an intermediate mapping function
System.out.println(IntStream.of(1,2,3).boxed()
.collect(Collectors.reducing(0, i->i*2, (i1,i2)->i1+i2)));
You can get this far with your own Collector
Collector<Integer, Integer, Integer> myctry = Collector.of(
()->0,
(i1,i2)->{
// what to do here?
},
(i1,i2)->{
return i1+i2;
}
);
The accumulator is A function that folds a value into a mutable result container with mutable being the keyword here.
So, make a mutable integer
public class MutableInteger {
private int value;
public MutableInteger(int value) {
this.value = value;
}
public void set(int value) {
this.value = value;
}
public int intValue() {
return value;
}
}
And now:
Collector<MutableInteger, MutableInteger, MutableInteger> myc = Collector.of(
()->new MutableInteger(0),
(i1,i2)->{
i1.set(i1.intValue()+i2.intValue());
},
(i1,i2)->{
i1.set(i1.intValue()+i2.intValue());
return i1;
}
);
And then:
System.out.println(IntStream.of(1,2,3)
.mapToObj(MutableInteger::new)
.collect(myc).intValue());
Reference:
Example of stream reduction with distinct combiner and accumulator
EDIT: The finisher just does whatever with the final result. If you don't set it on purpose then it is set by default to IDENTITY_FINISH which is Function.identity() which says just to return the final result as is.
EDIT: If you're really desperate:
Collector<int[], int[], int[]> mycai = Collector.of(
()->new int[1],
(i1,i2)->i1[0] += i2[0],
(i1,i2)->{i1[0] += i2[0]; return i1;}
);
System.out.println(IntStream.of(1,2,3)
.mapToObj(v->{
int[] i = new int[1];
i[0] = v;
return i;
})
.collect(mycai)[0]);
I Use this sample for search in Map but not work :|:
var xmenList = ['4','xmen','4xmen','test'];
var xmenObj = {
'first': '4',
'second': 'xmen',
'fifth': '4xmen',
'author': 'test'
};
print(xmenList.indexOf('4xmen')); // 2
print(xmenObj.indexOf('4xmen')); // ?
but I have error TypeError: xmenObj.indexOf$1 is not a function on last code line.
Pelease help me to search in map object simple way same as indexOf.
I found the answer:
print(xmenObj.values.toList().indexOf('4xmen')); // 2
or this:
var ind = xmenObj.values.toList().indexOf('4xmen') ;
print(xmenObj.keys.toList()[ind]); // fifth
Maps are not indexable by integers, so there is no operation corresponding to indexOf. If you see lists as specialized maps where the keys are always consecutive integers, then the corresponding operation should find the key for a given value.
Maps are not built for that, so iterating through all the keys and values is the only way to get that result.
I'd do that as:
K keyForValue<K, V>(Map<K, V> map, V value) {
for (var entry in map.entries) {
if (entry.value == value) return key;
}
return null;
}
The entries getter is introduced in Dart 2. If you don't have that, then using the map.values.toList().indexOf(value) to get the iteration position, and then map.keys.elementAt(thatIndex) to get the corresponding key.
If you really only want the numerical index, then you can skip that last step.
It's not amazingly efficient (you allocate a new list and copy all the values). Another approach is:
int indexOfValue<V>(Map<Object, V> map, V value) {
int i = 0;
for (var mapValue in map.values) {
if (mapValue == value) return i;
i++;
}
return -1;
}
You can search using .where(...) if you want to find all that match or firstWhere if you assume there can only be one or you only want the first
var found = xmenObj.keys.firstWhere(
(k) => xmenObj[k] == '4xmen', orElse: () => null);
print(xmenObj[found]);
Given a namedQuery:
class MyDomainObject {
String someProperty
static namedQueries = {
myNamedQuery {
// some implementation here
}
}
}
I can use it to generate a list, sorted by a single key, like this (documentation for 2.4.3 here):
def resultsList = MyDomainObject.myNamedQuery.list(sort: "someProperty", order: "desc")
How do I order the results by multiple columns? I'd like to be able to define the sort parameters dynamically, not define them in the query.
I'm sure there's a better way, but I ended up creating another named query that I can concatenate onto my chosen one (I could always incorporate into the original query too).
// expects to be passed a List containing a series of Maps
orderByMultipleColumns { List columnsToSortBy ->
columnsToSortBy.each { Map field ->
order("${field.fieldName}", field.fieldOrder)
}
}
// usage:
List orderByList = []
// ...
// within some loop that I use:
orderByList << [fieldName: someValue, fieldOrder: dir] // dir == 'asc' or 'desc'
// ...
MyDomainObject.myNamedQuery().orderByMultipleColumns(orderList).listDistinct(max: length, offset: start)
I try to implement mergeSort in dart. Here is my code:
List mergeSort(List list)
{
if (list.length <= 1) return list;
List left, right, result;
int middle = list.length ~/ 2;
left = mergeSort(list.getRange(0, middle));
right = mergeSort(list.getRange(middle, list.length) as List);
result = merge(left, right);
return result;
}
And I get an TypeError at this line:
left = mergeSort(list.getRange(0, middle));
It seems that getRange() returns Iterable, but List implements Iterable what is the problem here, shouldn't it work?
Anyway when I try:
left = mergeSort(list.getRange(0, middle) as List);
it still doesn't work and gives me a CastError.
So my question is what's the problem here, and what's the Dart-style solution for it?
Thx in advance.
Iterable.toList() should do the trick.
left = mergeSort(list.getRange(0, middle).toList());
You can't directly cast an Iterable to a List because Iterable is a super type of List.
You can cast an object to its parent class, but you cant cast an object to any of its child class.
Example:
var a = new AnyClass();
var o = new Object(); //Actually this is not correct, but let's imagine
(o as AnyClass).toString() //wont works
(a as Object).toString() //will works
The getRange method returns an Iterable, not a List. Using as List does not change that, it just fails because its operand isn't actually a List.
You can use the List.sublist method to get a slice of a list as an actual List.
The collection package on pub also contains an implementation of merge-sort: package:collection/algorithms.dart.
I have a Grails command object with a list of maps. The map key is intended to be a numeric domain object ID.
class MyCommand {
def grid = [].withDefault { [:] }
}
Data binding to the list/map is working in general because of the dynamic list expansion.
However, in the POST, the map keys are being bound as Strings and I want them to be Longs, as they are when the form is initially populated. I want foo[123] in my map, not foo['123'].
Alternatively I would be satisfied if the [] operators found the correct value given a numeric ID key to look up. In other words, if I could get foo[123] to return the same value as foo['123'], that would work too.
Any way to get this to work the way I want to? Maybe strongly type the map?
Or a better approach?
You can inject the property into the map to convert a String key to Long. For example:
def myMap = [:] << ['1': "name"] << ['Test': "bobo"]
def result = myMap.inject([:]){map, v ->
def newKey = v.key.isNumber() ? v.key.toLong() : v.key
map[newKey] = v.value
map
}
assert myMap['1'] == 'name'
assert result[1L] == 'name'
assert result['Test'] == 'bobo'