Sorting Guava table on values - java

Is there any possible way to do that? The expecting effect would be that rowMap and columnMap entry values would be sorted by value.
The problem is that I cannot create a comparator without the underlying maps in Table.
Table table = TreeBasedTable.create(?,?);
Map<String, Map<String, String>> rowMap = table.rowMap();
Map<String, String> thisMapShouldBeSortedByValues = rowMap.get(smth);
Map<String, Map<String, String>> columnMap = table.columnMap();
Map<String, String> thisMapShouldBeSortedByValues = columnMap.get(smth);
Now I always have to sort rowMap and columnMap afterwards and allocate new TreeMaps on that.

First of all, I am assuming that by thisMapShouldBeSortedByValues, you mean thisMapShouldBeSortedByKeys
TreeBasedTable extends RowSortedTable, so the table is only sorted by it's rows, not its columns. Calling table.columnMap() gives you an unordered Map<C, Map<R, V>.
Since it is sorted by rows, the table.rowMap returns a SortedMap<R, Map<C, V>>. So the map is sorted on it's row keys, but the Map<C, V> value is an unsorted map, hence why calls to rowMap.get() returns an unordered map.
The SortedMap you are trying to get from calling table.rowMap(), and then rowMap.get(), can be obtained by instead calling table.rowKeySet() and table.row(R rowKey) like so (You build the map you originally wanted to get by calling table.rowMap():
ImmutableSortedMap.Builder<String, SortedMap<String, String>> builder = ImmutableSortedMap.builder();
for (String rowKey : table.rowKeySet()){
builder.put(rowKey, table.row(rowKey)) ;
}
SortedMap<String, <SortedMap<String, String>> thisMapIsSortedByKeys = builder.build();

Related

Sort every Map in List by Set of keys

I have List of LinkedHashMap like List<Map<String, String>>. Every Map has the same number of elements and every Map has the same keys.
The second element is LinkedHashSet<String> - set of keys.
Now I would like to order every Map from List by keys. Sort ordering is in LinkedHashSet<String>.
My attempt is iterate by List<Map<String, String>>. For every Map create new Map and iterate by Set. To the new Map put key and value from old Mapwhere key is taken from Set. In code:
private List<Map<String, String>> sort(List<Map<String,String> result, LinkedHashSet<String> keys){
List<Map<String, String>> sortedResult = new LinkedList<>();
result.forEach(map -> {
Map<String, String> sortedMap = new LinkedHashMap<>();
keys.forEach(key -> {
sortedMap.put(key, map.get(key));
});
sortedResult.add(sortedMap);
});
return sortedResult;
}
I think it is a little bit complicated and in my opinion there exsists better way to do that.
You have a LinkedHashMap which tries to maintain only the order of insertion of keys, not the natural-ordering of keys. One thing you can do is to maintain a list of keys outside the map and sort them and re-insert the <key,value> pairs as per the order of the sorted list of keys. So it seems you are already doing this in your code by having an order defined by LinkedHashSet.
The other simple approach is:
If you want an ordered map by keys, you most probably need a TreeMap, insertion into this map maintains the natural ordering of keys and you can construct a treemap from an existing map.
private List<Map<String, String>> sort(List<Map<String,String> result) {
List<Map<String, String>> sortedResult = new LinkedList<>();
for ( Map<String, String> m : result )
sortedResult.add(new TreeMap(m)));
return sortedResult;
}
BTW, Local variables referenced from a lambda expression must be final
There are a couple of things I would change:
The argument name "result" is misleading. People going over the code quickly will think this is the returned result. I would change it to "unsortedMaps" or something similar
Each Map shouldn't affect the other so instead of result.forEach you could use result.parallelStream().forEach to make every map sorted in it's own thread. You will need to make insertion to list itself thread safe (either surronding "sortedResult.add(sortedMap" with synchronized statment or use a thread-safe list implementation. All this doesn't guarantee improvement in performance. It depends on many variants such as the size of the collections and number of cores. Test it to find out.
There are a lot of details in this function. I would extract the part dealing with each map to a seperate function
Here is the result (didn't test the code so can't gurentee correctness. Needless to say unit-tests are always the way to go):
private List<Map<String, String>> sort(List<Map<String,String>> unsortedMaps, LinkedHashSet<String> keys){
List<Map<String, String>> sortedResult = new LinkedList<>();
unsortedMaps.parallelStream().forEach(map -> {
Map<String, String> sortedMap = getSortedMap(keys, map);
synchronized (sortedResult) {
sortedResult.add(sortedMap);
}
});
return sortedResult;
}
private Map<String, String> getSortedMap(LinkedHashSet<String> keys, Map<String, String> map) {
Map<String, String> sortedMap = new LinkedHashMap<>();
keys.forEach(key -> {
sortedMap.put(key, map.get(key));
});
return sortedMap;
}
To complete SomeDude answer, if the natural order isn't enough for your need, you can try to specify a Comparator to the TreeMap :
private List<Map<String, String>> sort(List<Map<String,String>> mapList, Set<String> keys){
List<String> keysList = new ArrayList<>(keys);
return mapList.stream().map(map -> copyAndReOrderMap(map, keysList)).collect(Collectors.toList());
}
private Map<String, String> copyAndReOrderMap(Map<String, String> map, List<String> keysList) {
Map<String, String> orderedMap = new TreeMap<>((key1, key2) -> Integer.compare(keysList.indexOf(key1), keysList.indexOf(key2)));
orderedMap.putAll(map);
return orderedMap;
}
NB: Unless you deal with very large maps, i don't see why you would want to sort each map in a separate Thread.

How do I reverse the values of a Map when one type is a Set of Integers

For example how do I reverse the key and value datatypes from something like this:
TreeMap<Set<Integer>, Integer>
...into this:
TreeMap<Integer, Set<Integer>>
If you're using Java8, you can do:
TreeMap<Set<Integer>, Integer> map = ....;
Map<Integer, Set<Integer>> result =
map.entrySet()
.stream()
.map(entry -> new AbstractMap.SimpleEntry(entry.getValue(), entry.getKey())
.collect(Collectors.toMap());
Iterate over the entry set and put them reversely in a new TreeMap. The following generic method will reverse every map.
public static <K, V> TreeMap<V, K> reverse(TreeMap<K, V> map) {
TreeMap<V, K> result = new TreeMap<V, K>();
for (Entry<K, V> e : map.entrySet())
result.put(e.getValue(), e.getKey());
return result;
}
Important
If a value is present two times, you will lose the key with the smallest value.
Example:
public static void main(String[] args) {
TreeMap<Integer, Integer> map = new TreeMap<Integer, Integer>();
map.put(1, 2);
map.put(2, 2);
System.out.println(reverse(map));
}
Output:
{2=2}
The tuple {1,2} is lost since the value 2 was presents multiple times!
Maps are not easilly reversible, And rightly so.
Reversing maps might not work.
In a Map Keys are obviously unique but values have no such constraint.
I don't know what the problem you're facing is, but it sounds as if there is a better design to solve it.
Perhaps you should use a BiMap?
It's essentialy a two way map in which both the "keys" and the "values" are unique. It allows efficient lookup both from the keys and from the values.
Java doesn't have a built in BiMap but google Guava and Apache Commons do.

Create a map from a list of maps

I have a list of maps.
List<Map<Integer, String>>
The values in the list are, for example
<1, String1>
<2, String2>
<1, String3>
<2, String4>
As an end result, I want a Map>, like
<1, <String1, String3>>
<2, <String2, String4>>
How can I achieve this in Java.
CODE :
List<Map<Integer, String>> genericList = new ArrayList<Map<Integer,String>>();
for(TrackActivity activity : activityMajor){
Map<Integer, String> mapIdResponse = activity.getMapIdResponse();
genericList.add(mapIdResponse);
}
Now this genericList is the input and from this list, based on the same ids I want a
Map<Integer, List<String>> mapIdResponseList
Basically, to club the responses which are String based on the ids, grouping the responses with same id in a list and then creating a new map with that id as the key and the list as its value.
You can do it the following with Java 8:
private void init() {
List<Map<Integer, String>> mapList = new ArrayList<>();
Map<Integer, String> map1 = new HashMap<>();
map1.put(1, "String1");
mapList.add(map1);
Map<Integer, String> map2 = new HashMap<>();
map2.put(2, "String2");
mapList.add(map2);
Map<Integer, String> map3 = new HashMap<>();
map3.put(1, "String3");
mapList.add(map3);
Map<Integer, String> map4 = new HashMap<>();
map4.put(2, "String4");
mapList.add(map4);
Map<Integer, List<String>> response = mapList.stream()
.flatMap(map -> map.entrySet().stream())
.collect(
Collectors.groupingBy(
Map.Entry::getKey,
Collectors.mapping(
Map.Entry::getValue,
Collectors.toList()
)
)
);
response.forEach((i, l) -> {
System.out.println("Integer: " + i + " / List: " + l);
});
}
This will print:
Integer: 1 / List: [String1, String3]
Integer: 2 / List: [String2, String4]
Explanation (heavily warranted), I am afraid I cannot explain every single detail, you need to understand the basics of the Stream and Collectors API introduced in Java 8 first:
Obtain a Stream<Map<Integer, String>> from the mapList.
Apply the flatMap operator, which roughly maps a stream into an already existing stream.
Here: I convert all Map<Integer, String> to Stream<Map.Entry<Integer, String>> and add them to the existing stream, thus now it is also of type Stream<Map.Entry<Integer, String>>.
I intend to collect the Stream<Map.Entry<Integer, String>> into a Map<Integer, List<String>>.
For this I will use a Collectors.groupingBy, which produces a Map<K, List<V>> based on a grouping function, a Function that maps the Map.Entry<Integer, String> to an Integer in this case.
For this I use a method reference, which exactly does what I want, namely Map.Entry::getKey, it operates on a Map.Entry and returns an Integer.
At this point I would have had a Map<Integer, List<Map.Entry<Integer, String>>> if I had not done any extra processing.
To ensure that I get the correct signature, I must add a downstream to the Collectors.groupingBy, which has to provide a collector.
For this downstream I use a collector that maps my Map.Entry entries to their String values via the reference Map.Entry::getValue.
I also need to specify how they are being collected, which is just a Collectors.toList() here, as I want to add them to a list.
And this is how we get a Map<Integer, List,String>>.
Have a look at guavas MultiMap. Should be exactly what you are looking for:
http://code.google.com/p/guava-libraries/wiki/NewCollectionTypesExplained#Multimap

Collections and Naturally ordering a Map<Long, Map<String, String>>

I'm having trouble with the more intricate map; for a standard Map<Long, String>, I would do something like:
Ordering<Long> valueComparator = Ordering.natural().onResultOf(Functions.forMap(myMap));
Map<Long, String> orderedMap = ImmutableSortedMap.copyOf(myMap, valueComparator);
But I can't seem to get it to like Map<Long, Map<String, String>>, still ordered by Long. Maybe I'm missing something? Below doesn't work...
Ordering<Long> valueComparator = Ordering.natural().onResultOf(Functions.forMap(myOtherMap));
Map<Long, Map<String, String>> orderedMyOtherMap = ImmutableSortedMap.copyOf(myOtherMap,valueComparator);
Your first example isn't doing what you seem to be saying it's doing. It's creating a map that's ordered by the String values corresponding to each Long key. If you wanted to just order by the keys, you'd just do:
ImmutableSortedMap<Long, String> orderedMap = ImmutableSortedMap.copyOf(myMap);
The same thing would work for a Map<Long, Map<String, String>>. The reason what you're trying to do doesn't work is that a Map<String, String> is not Comparable, so there is no natural ordering for it.
As an aside, you may want to consider using a Table<Long, String, String> rather than a Map<Long, Map<String, String>>. There's even a TreeBasedTable that will store the row and column keys in sorted order.

How do I convert a Map to List in Java?

How do I convert a Map<key,value> to a List<value>? Should I iterate over all map values and insert them into a list?
List<Value> list = new ArrayList<Value>(map.values());
assuming:
Map<Key,Value> map;
The issue here is that Map has two values (a key and value), while a List only has one value (an element).
Therefore, the best that can be done is to either get a List of the keys or the values. (Unless we make a wrapper to hold on to the key/value pair).
Say we have a Map:
Map<String, String> m = new HashMap<String, String>();
m.put("Hello", "World");
m.put("Apple", "3.14");
m.put("Another", "Element");
The keys as a List can be obtained by creating a new ArrayList from a Set returned by the Map.keySet method:
List<String> list = new ArrayList<String>(m.keySet());
While the values as a List can be obtained creating a new ArrayList from a Collection returned by the Map.values method:
List<String> list = new ArrayList<String>(m.values());
The result of getting the List of keys:
Apple
Another
Hello
The result of getting the List of values:
3.14
Element
World
Using the Java 8 Streams API.
List<Value> values = map.values().stream().collect(Collectors.toList());
map.entrySet() gives you a collection of Map.Entry objects containing both key and value. you can then transform this into any collection object you like, such as new ArrayList(map.entrySet());
a list of what ?
Assuming map is your instance of Map
map.values() will return a Collection containing all of the map's values.
map.keySet() will return a Set containing all of the map's keys.
I guess you want to convert the values contained in the Map to a list? Easiest is to call the values() method of the Map interface. This will return the Collection of value objects contained in the Map.
Note that this Collection is backed by the Map object and any changes to the Map object will reflect here. So if you want a separate copy not bound to your Map object, simply create a new List object like an ArrayList passing the value Collection as below.
ArrayList<String> list = new ArrayList<String>(map.values());
Map<String, Integer> map = new HashMap<String, Integer>();
map.put("java", 20);
map.put("C++", 45);
Set <Entry<String, Integer>> set = map.entrySet();
List<Entry<String, Integer>> list = new ArrayList<Entry<String, Integer>>(set);
we can have both key and value pair in list.Also can get key and value using Map.Entry by iterating over list.
If you want to ensure the values in the resultant List<Value> are in the key-ordering of the input Map<Key, Value>, you need to "go via" SortedMap somehow.
Either start with a concrete SortedMap implementation (Such as TreeMap) or insert your input Map into a SortedMap before converting that to List. e.g.:
Map<Key,Value> map;
List<Value> list = new ArrayList<Value>( new TreeMap<Key Value>( map ));
Otherwise you'll get whatever native ordering the Map implementation provides, which can often be something other than the natural key ordering (Try Hashtable or ConcurrentHashMap, for variety).
// you can use this
List<Value> list = new ArrayList<Value>(map.values());
// or you may use
List<Value> list = new ArrayList<Value>();
for (Map.Entry<String, String> entry : map.entrySet())
{
list.add(entry.getValue());
}
Map<String, String > map = new HapshMap<String, String>;
map.add("one","java");
map.add("two", "spring");
Set<Entry<String, String>> set = map.entrySet();
List<Entry<String, String>> list = new ArrayList<Entry<String, String>> (set);
for(Entry<String, String> entry : list) {
System.out.println(entry.getKey());
System.out.println(entry.getValue());
}
Here's the generic method to get values from map.
public static <T> List<T> ValueListFromMap(HashMap<String, T> map) {
List<T> thingList = new ArrayList<>();
for (Map.Entry<String, T> entry : map.entrySet()) {
thingList.add(entry.getValue());
}
return thingList;
}
public List<Object> convertMapToList(Map<Object, Object> map){
return new ArrayList<>(map.values());
}
If you want an immutable copy of the values:
List<Value> list = List.copyOf(map.values())

Categories