Traversing a HashMap by its sorted keys - elasticsearch-painless

I try to figure out how I could iterate on a Painless HashMap values by its keys sorted in ascending order, the following doesn't work:
HashMap buckets;
for(String bucketKey : new TreeSet(buckets.keySet())) {
// actual code
}

Finally found a way to do this by using ArrayList but absolutely not sure it is the way to go:
HashMap buckets;
ArrayList l = new ArrayList(buckets.keySet());
Collections.sort(l);
for(String bucketKey : l) {
// actual code
}

Related

Query regarding merging values of a Map to the first key of the Map

I've a hashmap with few key value pairs.
My requirement is to iterate over the all the key and value pairs but merge all the value to the first key of the hashset.
For example,
Map<String,Integer> resultMap = new HashMap();
Map<String,Integer> map = new HashMap();
map.put("abc",1);
map.put("def",2);
map.put("efg",3);
map.put("uvw",4);
map.put("xyz",5);
I want to do something similar to this:
map.foreach((k,v)->resultMap.merge(k,v,(v1,v2)->v1+v2)
the resultant map will have only one key i.e. "abc" and value as (1+2+3+4+5)=15
How can I do this efficiently using java8?
You can do like this:
String firstKey = "";
for (Map.Entry<String,Integer> entry:map.entrySet()) {
if (resultMap.isEmpty()) firstKey = entry.getKey();
resultMap.merge(firstKey, entry.getValue(), Integer::sum);
}
stream version:
resultMap = map.entrySet().stream()
.collect(HashMap::new, (m, e) ->
{
if (m.isEmpty()) m.put(e.getKey(), e.getValue());
else m.merge(m.entrySet().stream()
.findFirst().get().getKey(),e.getValue(),Integer::sum);
}
, HashMap::putAll);
You can sum all the values of the map first and then add it resultMap with first key.
int sum = map.values().stream().reduce(0, Integer::sum);
Map.Entry<String,Integer> entry = map.entrySet().iterator().next();
String key = entry.getKey();
resultMap.put(key,sum);
Using Merge function, first sum all the values with "randomKey" not present in the original Map. Then replace random key with original map's first key
map.forEach((k,v)->resultMap.merge("randomKey",v,Integer::sum));
Map.Entry<String,Integer> entry = map.entrySet().iterator().next();
Object obj = resultMap.remove("randomKey");
resultMap.put(entry.getKey(), (Integer) obj);

How to use Stream, to write an efficient shuffling method

I have an ArrayList of Residence objects. Each Residence object has two fields, type::String, and price::BigInteger. I was wondering if there is an efficient way to restructure the list, in such a way, so no Residence object with the same name is next to each other. The goal is to write an efficient, shuffling method.
I suggest you use a HashMap<String, Stack<Residence>> and save the corresponding element for each type.
Then loop the hashmap through the keys in a Round Robin way and pop the item from the stack. Each item you get, you can add it to a new list.
Assuming your ArrayList of Residence is residences, the code should be something like this (Not tested, only for show the algorithm):
HashMap<String, Stak<Residence>> hm;
ArrayList<Residence> resultList = new ArrayList();
for (Residence r : residences) {
hm.put(r.type, r);
}
boolean exist = true;
while(exist) {
exist = false;
for(Map.Entry m : hm.entrySet()){
if(!m.getValue().isEmpty()) {
exist = true;
resultList.add(m.getValue().pop());
}
}
}

how to convert forEach to lambda

Iterator<Rate> rateIt = rates.iterator();
int lastRateOBP = 0;
while (rateIt.hasNext())
{
Rate rate = rateIt.next();
int currentOBP = rate.getPersonCount();
if (currentOBP == lastRateOBP)
{
rateIt.remove();
continue;
}
lastRateOBP = currentOBP;
}
how can i use above code convert to lambda by stream of java 8? such as list.stream().filter().....but i need to operation list.
The simplest solution is
Set<Integer> seen = new HashSet<>();
rates.removeIf(rate -> !seen.add(rate.getPersonCount()));
it utilizes the fact that Set.add will return false if the value is already in the Set, i.e. has been already encountered. Since these are the elements you want to remove, all you have to do is negating it.
If keeping an arbitrary Rate instance for each group with the same person count is sufficient, there is no sorting needed for this solution.
Like with your original Iterator-based solution, it relies on the mutability of your original Collection.
If you really want distinct and sorted as you say in your comments, than it is as simple as :
TreeSet<Rate> sorted = rates.stream()
.collect(Collectors.toCollection(() ->
new TreeSet<>(Comparator.comparing(Rate::getPersonCount))));
But notice that in your example with an iterator you are not removing duplicates, but only duplicates that are continuous (I've exemplified that in the comment to your question).
EDIT
It seems that you want distinct by a Function; or in simpler words you want distinct elements by personCount, but in case of a clash you want to take the max pos.
Such a thing is not yet available in jdk. But it might be, see this.
Since you want them sorted and distinct by key, we can emulate that with:
Collection<Rate> sorted = rates.stream()
.collect(Collectors.toMap(Rate::getPersonCount,
Function.identity(),
(left, right) -> {
return left.getLos() > right.getLos() ? left : right;
},
TreeMap::new))
.values();
System.out.println(sorted);
On the other hand if you absolutely need to return a TreeSet to actually denote that this are unique elements and sorted:
TreeSet<Rate> sorted = rates.stream()
.collect(Collectors.collectingAndThen(
Collectors.toMap(Rate::getPersonCount,
Function.identity(),
(left, right) -> {
return left.getLos() > right.getLos() ? left : right;
},
TreeMap::new),
map -> {
TreeSet<Rate> set = new TreeSet<>(Comparator.comparing(Rate::getPersonCount));
set.addAll(map.values());
return set;
}));
This should work if your Rate type has natural ordering (i.e. implements Comparable):
List<Rate> l = rates.stream()
.distinct()
.sorted()
.collect(Collectors.toList());
If not, use a lambda as a custom comparator:
List<Rate> l = rates.stream()
.distinct()
.sorted( (r1,r2) -> ...some code to compare two rates... )
.collect(Collectors.toList());
It may be possible to remove the call to sorted if you just need to remove duplicates.

How to sort a list of strings by using the order of the items in another list?

I want to sort a list of strings (with possibly duplicate entries) by using as ordering reference the order of the entries in another list. So, the following list is the list I want to sort
List<String> list = ['apple','pear','apple','x','x','orange','x','pear'];
And the list that specifies the order is
List<String> order = ['orange','apple','x','pear'];
And the output should be
List<String> result = ['orange','apple','apple','x','x','x','pear','pear'];
Is there a clean way of doing this?
I don't understand if I can use list's sort and compare with the following problem. I tried using map, iterable, intersection, etc.
There might be a more efficient way but at least you get the desired result:
main() {
List<String> list = ['apple','pear','apple','x','x','orange','x','pear'];
List<String> order = ['orange','apple','x','pear'];
list.sort((a, b) => order.indexOf(a).compareTo(order.indexOf(b)));
print(list);
}
Try it on DartPad
The closure passed to list.sort(...) is a custom comparer which instead of comparing the passed item, compares their position in order and returns the result.
Using a map for better lookup performance:
main() {
List<String> list = ['apple','pear','apple','x','x','orange','x','pear'];
List<String> orderList = ['orange','apple','x','pear'];
Map<String,int> order = new Map.fromIterable(
orderList, key: (key) => key, value: (key) => orderList.indexOf(key));
list.sort((a, b) => order[a].compareTo(order[b]));
print(list);
}
Try it on DartPad

sort a list of #Sortable objects in descending order

I have a class
#Sortable(includes = ['date'])
class Item {
// other fields not relevant to this question
Date date
}
If I sort a List of these objects it will sort them in ascending order based on the date field. Is there a way to sort them in descending order instead? I know I could just call reverse() on the result of the ascending sort, but this seems a bit inefficient
Here are a couple of ways:
def items = [
new Item(date: new Date(40000)),
new Item(date: new Date(1000)),
new Item(date: new Date(200000)),
new Item(date: new Date(00100)),
]
items.sort { a, b -> b <=> a }
items.sort(true, Collections.reverseOrder())

Resources