I created a Guava Multimap
ListMultimap<String,String> sampleMultimap = ArrayListMultimap.create();
And added some values which I take from a text file.
Now as an output I have
{Football=[10], Basketball=[1210, 1210, 1210, 120], Tennis=[1, 10, 100, 1000]}
I would like to sum each of the sports and find the highest sum. So expected result is
Basketball: 3750
Somehow I am stuck because I cant get the each one with samleMultimap.get(x) because key values are unknown and comes form text file.
Is there any way to get the expected result?
In the end you want Map<String, Integer> where values are sums of original multimap. You can use couple views:
Multimap#asMap() to have Map<K, Collection<V>> view of your multimap,
Maps#transformValues(Map, Function) - to create a view of a map where each value is transformed by a function.
Map<String, Integer> map = Maps.transformValues(multimap.asMap(), ints ->
ints.stream().mapToInt(Integer::intValue).sum());
Note that if you want to perform many operations on resulted map, read caveat in documentation and for example do ImmutableMap.copyOf(map):
The function is applied lazily, invoked when needed. This is necessary for the returned map to be a view, but it means that the function will be applied many times for bulk operations like Map.containsValue(java.lang.Object) and Map.toString(). For this to perform well, function should be fast. To avoid lazy evaluation when the returned map doesn't need to be a view, copy the returned map into a new map of your choosing.
With a ListMultimap you can call asMap() to get a view as a java.util.Map, then do normal map things.
If you change sampleMultimap to be an ArrayListMultimap instead of ListMultimap, then you can call keySet() directly (skip the asMap() call) and iterate over the resulting keys.
Here's an example showing how you could get each key from a ListMultimap (even if the underlying implementation is an instance of ArrayListMultimap), then get each associated value:
ListMultimap<String,String> sampleMultimap = ArrayListMultimap.create();
Map<String,String> mapView = sampleMultimap.asMap();
for (String key : mapView.keySet()) {
String value = mapView.get(key);
// do something with value
}
Related
I want to do a "group-by" on arrayList of HashMap Data structure. As my data is not fixed, so I don't have any fixed classes.
Data is shown as below.
[{"name":"laxman","state":"Karnataka","Mobile":9034782882},
{"name":"rahul","state":"Kerala","Mobile":9034782882},
{"name":"laxman","state":"karnataka","Mobile":9034782882},
{"name":"ram","state":"delhi","Mobile":9034782882}]
The above keys are not fixed, So, I can't have classes for it.
Data and formulas will be dynamical. But for now, I am taking this example to understand Stream.Collector on this data.
Now, I want to get the count on basis of name and state,
So basically I want to group-by on name and state and want to get count.
I tried to use Stream.Collector but am not able to achieve what I want.
You can accomplish this with Collectors.groupingBy, using a List as the key of the returned Map:
Map<List<String>, Long> result = yourListOfMaps.stream()
.collect(Collectors.groupingBy(
m -> Arrays.asList(String.valueOf(m.get("name")), String.valueOf(m.get("state"))),
Collectors.counting()));
This works well because all implementations of List in Java implement hashCode and equals consistently, which is a must for every class that is to be used as the key of any Map implementation.
You have to do groupingBy twice once on the key and once again on the value.
Map<String, Map<Object, Long>> map = listOfMap.stream().flatMap(a -> a.entrySet().stream())
.collect(Collectors.groupingBy(Map.Entry<String, String>::getKey,
Collectors.groupingBy(Map.Entry::getValue, Collectors.counting())));
Output
{mobile={9034782882=4}, name={rahul=1, laxman=2, ram=1}, state={Karnataka=2, delhi=1, Kerala=1}}
I have multiple files which contains key=value string pairs. The keys are the same between the files, but the values differs. Each file can have 1000 plus of such pairs.
I want to store each file in a separate hashmap, ie map<KeyString, ValueString>, so if there are five files, then there will be five hashmaps.
To avoid duplicating the keys across each hashmap, is it possible to have each map reference the same key? Note that once the keys are added to the map, it will not be deleted.
I considered making the first file the 'base' as in the flyweight pattern, this base would be the intrinsic set of keys/values. The other remaining files would be the extrinsic set of values, but I don't know how to relate the values back to the base (intrinsic) keys without duplicating the keys?
I am open to a simpler/better approach.
I can think about a simpler approach. Instead of having Map<String, String> think of Map<String, List<String> or directly MultiMap<String, String> from guava.
If each key is in each file and all have values, you could store values from first file at 0th index, from the second at 1st index etc.
If it wouldn't work, I recommend a Collection<Map<String, String>, so you're able to iterate through your Maps. Then when you want to add value to one of the Maps, go through all keySets and if one of them contains that key, just put with object returned from this keySet.
Other solution would be to have a HashSet of keys that have already been put. This would be more efficient.
After reading in the keys, you can use String.intern().
When called, what it does is either:
add the String to the internal pool if it didn't exist already;
return the equivalent String from the pool if it already existed.
String#intern Javadoc
First of all, I don't see the problem with storing multiple instances of your String keys. 5 HashMaps * 1000 keys is a very small number, and you shouldn't have memory issues.
That said, if you still want to avoid duplicating the Strings, you can create the first HashMap, and then you the exact same keys for the other HashMaps.
For example, suppose map1 is the first HashMap and it is already populated with the contents of the first file.
You can write something like this to populate the 2nd HashMap:
for (String key : map1.keySet()) {
map2.put (key, someValue);
}
Of course you will have to find for each key of the first map the corresponding value of the second map. If the keys are not stored in the same order in the input files, this may require some preliminary sorting step.
Perhaps you could hold a static Map<> to map your keys to unique Integers and use those Integers for the keys to your map?
Something like:
class KeySharedMap<K,V> {
// The next key to use. Using Atomics for the auto-increment.
static final AtomicInteger next = new AtomicInteger(0);
// Static mapping of keys to unique Integers.
static final ConcurrentMap<Object,Integer> keys = new ConcurrentHashMap<>();
// The map indexed by Integer from the `keys`.
Map<Integer, V> map = new HashMap<>();
public V get(Object key) {
return map.get(keys.get(key));
}
public V put(Object key, V value) {
// Associate a unique integer for each unique key.
keys.computeIfAbsent(key,x -> next.getAndIncrement());
// Put it in my map.
return map.put(keys.get(key),value);
}
}
Yes, I realise that K is not used here but I suspect it would be necessary if you wish to implement Map<K,V>.
I have an ArrayList of Objects. The object has five fields: id, date, name, value, adjusted_value. The list may hold multiple entries with the same name, and I am having trouble devising an efficient way to condense the list based on the name to where I will a list of similar objects but the values stored will be name, sum(value), sum(adjusted_value).
Is there an efficient way to do this? My current method has for loops inside of a do-while.
Clarfication:
I have a list of obejcts :
{id,date,name,value,ajusted_value},
{1,"10/30/2014","peaches",4,3}
{2,"10/30/2014","apples",2,2}
{3,"10/31/2014","peaches",3,1}
.
.
.
I want to condense to list based the name value to one that looks like this:
{null,null,"peaches",7,4}
{null,null,"apples",2,2}
.
.
.
However, I found that HashMap's put() functionality will perform what I desire automatically, but now I need to do this sort of action in Javascript if possible.
You can define a Map where the key is the name and value is the object instance.
Go through the list and for each instance check whether it exists in the map.
If not just add to the map. map.put(instance.name,instance)
If it's already added to the map just
mapInstance=map.get(instance.name);
mapInstance.value+=instance.value;
mapInstance.adjusted_value+=instance.adjusted_value;
After the loop you have the filled map with grouped data
I would use Guava in two step. Use a NameFunction to convert the list to a Multimap. Use a CondenseFunction to convert the values of the Multimap.
Function<MyClass, String> toName = new Function(){
public String apply(MyClass input){return input.name;}};
ImmutableListMultimap<String, MyClass> multimap = Multimaps.index(myList, toName);
Map<String, Collection<MyClass>> map = multimap.asMap();
Function<Collection<MyClass>, MyClass> condense= new Function(){
public MyClass apply(Collection<MyClass>input){
// create sums here
}};
Map<String, MyClass> condensed = Maps.transformValues(map, condense);
Collection<MyClass> result = condensed.getValues();
Multimaps.index()
Maps.transformValues
I have something along the lines of this:
public HashMap<Boolean, String> map = new HashMap();
map.put(this.inverted, "Inverted");
map.put(this.active, "Loading");
System.out.println(map.size());
after seeing that the size was always 1, I realised that using map.put was overriding the previous data. I am currently trying to iterate over the hashmap. Is there a way to add mappings to it without overriding previous ones?
You have declared your HashMap as: -
public HashMap<Boolean, String> map = new HashMap();
Now, just think how many maximum mapping can you have in your map? The answer you can get by thinking of, what all values can your Boolean type take. This is because, you cannot have duplicate keys in a HashMap.
So, probably you got it now, that you can at max have only 2 mappings in your map, one for true and other for false(In fact you can have a 3rd one too, as you can have a mapping for a null key too in your HashMap).
So, in your case, if both this.inverted and this.active are either true or false. Then only one of them can be there, and that would be the later value inserted.
Is there a way to add mappings to it without overriding previous ones?
Probably you have build your HashMap wrongly. You should declare your map as: -
private Map<String, Boolean> map = new HashMap();
And now you can put two mappings as: -
map.put("Inverted", this.inverted);
map.put("Loading", this.active);
It's because this.inverted.equals(this.active) and this.inverted.hashcode()==this.active.hashcode()
Maybe you need redefine the equals method for the key.
In MAP
An object that maps keys to values. A map cannot contain duplicate keys; each key can map to at most one value. ---> from Map Api
from your implementation, may be this.inverted and this.active both have same value.
Check the input once. print the keySet, then check.
or change the input to Map<String, Boolean>
As #Frank suggest you should invert your Map.
public final Map<String, Boolean> map = new HashMap<>();
map.put("Inverted", this.inverted);
map.put("Loading", this.active);
System.out.println(map);
If the keys are the same than the previous value is overwritten in a standard Java Map. If you don't want this, you can have a look at a multimap which is implemented for example in commons-collections. It can hold different values for one key.
Hashmap is based on key/value pairs. If your keys are equal (they have the same hashcode), it will behave as you described.
For your use case, reversing your key/value pairs will help you.
public HashMap<String, Boolean> map = new HashMap();
map.put("Inverted", this.inverted);
map.put("Loading", this.active);
System.out.println(map.size());
Get object of innermap ,by passing outer map key .. Then check if key of innermap exists then update values with previous data. else create new object of inner map.
Is it possible in Guava,
To conduct a reverse lookup in BiMap for key and multiple values? Precisely, I have key and corresponding multiple values, I want to get key from a value.
To store multiple values in LinkedHashMap? Precisely, I want to store, key - multiple values in some order thus I can get key position in the list.
Ad. 1. Yes, it's possible to do a reverse lookup with a BiMap<K, V>, you just call inverse on your BiMap and you get inversed BiMap<V, K> view of your BiMap.
Example (taken from Guava's test suite):
public void testMapConstructor() {
/* Test with non-empty Map. */
Map<String, String> map = ImmutableMap.of(
"canada", "dollar",
"chile", "peso",
"switzerland", "franc");
HashBiMap<String, String> bimap = HashBiMap.create(map);
assertEquals("dollar", bimap.get("canada"));
assertEquals("canada", bimap.inverse().get("dollar"));
}
Ad. 2. Assuming you mean "I want to store, key -> multiple [collection] values" (Map<K, Collection<V>>), ListMultimap is probably what you want, more precisly ArrayListMultimap (preserves values order) or LinkedListMultimap (preserves both keys and values order). If your object is going to be immutable, I strongly advice you use ImmutableListMultimap.
You can also create your own implementation of Multimap by using factory (bit verbose), i.e. I use:
private static <K, V> ListMultimap<K, V> makeLinkedArrayListMultimap() {
return Multimaps.newListMultimap(Maps.<K, Collection<V>>newLinkedHashMap(),
new Supplier<List<V>>() {
#Override public List<V> get() {
return Lists.newArrayList();
}
});
}
public static void main(final String[] args) {
final ListMultimap<String, String> multimap = makeLinkedArrayListMultimap();
multimap.putAll("one", ImmutableList.of("zero", "three"));
multimap.putAll("two", ImmutableList.of("three", "four", "three"));
multimap.putAll("three", ImmutableList.<String>of()); // note that this doesn't add key to multimap
multimap.put("four", "forty-two");
System.out.println(multimap);
// prints {one=[one, three], two=[three, four, three], four=[forty-two]}
final List<String> listForOnes = multimap.get("one");
System.out.println(listForOnes.get(0));
// prints zero
}
P.S. Take a look at Guava's wiki, which is explaining both BiMap and Multimap.
The closest in Guava is Multiset to map multiple values to key, but I doubt it satisfies your requirement.
I doubt it is good idea to look up key using values (when you have multiple values mapped to single key), in order to do this your value should be unique and considering your data structure (which is like Map<Key, Collection<Value>) it cannot be guaranteed to have unique values.
The another option with guava is BiMap which requires unique values and can provide a reverse mappings (value -> key) but since you need to map multiple values to same key, this also not a good fit.
As #Xaerxess says in his answer to your 2nd question, you can make your own ListMultimap that uses a LinkedHashMap as its backing map using the Multimaps.newListMultimap method.
For your 1st question, where you have keys mapped to multiple values (i.e. a Multimap), you can use the method Multimaps.invertFrom to create an inverted copy of your original Multimap to do inverse lookups on. Also, you can create an ImmutableListMultimap copy of the original and use its inverse() method to get the inverse, though that's just going to copy the original just like Multimaps.invertFrom does (though it will cache it so repeated calls to inverse() return the same copy.)
This is likely worth it if you don't mind the extra memory consumption, are going to want to do multiple inverse lookups, and don't need the inverse copy to stay up to date with changes to the original that happen after you create it. If you just want to lookup the keys that map to one specific value, you can do that in one iteration of the entries without creating a full copy.