Obtain in Java from a Map, a Set with unique elements - java

I have this Map<String, Set<String>>:
{upload=[ADMIN], configure.accounts=[ADMIN, CONSULT], consult=[ADMIN, CONSULT], configure.merchants=[MANAGER, ADMIN], configure.invoices=[MANAGER, ADMIN], graphics=[MANAGER, ADMIN]}
and I want to obtain a structure Set<String> like the next one:
[ADMIN, CONSULT, MANAGER]
Where the data is filtered obtaining all the possible values from the Set within the Map. I can do this if I iterate the entire Map and compares the values, adding those that are not currently in the Set.
Is there some function to do this?

You do not have to check whether the individual values are already in the Set -- the set will take care of that, that's what sets are for. Just iterate the values and add them to the set of all values.
Map<String, Set<String>> data = new HashMap<>();
data.put("accounts", new HashSet<>(Arrays.asList("ADMIN", "CONSULT")));
data.put("merchants", new HashSet<>(Arrays.asList("MANAGER", "ADMIN")));
Set<String> values = new HashSet<>();
for (Set<String> set : data.values()) {
values.addAll(set);
}
If you are using Java 8, you can also replace the for loop with a foreach statement:
data.values().forEach(values::addAll);
Afterwards, values is [MANAGER, ADMIN, CONSULT].

Java 8 solution:
Map<String, Set<String>> test;
Set<String> set = test.values()
.stream()
.flatMap(t->t.stream())
.collect(Collectors.toSet());
No matter if you use Java 8 or Java lower than 8. Your end goal is to receive a Set<String> of elements because the elements in the Set<> are distinct.
Observing through the stream. First we are iterating over the values of the collection which are of type List. Then we use flatMap in order to transform this Set into a Stream of their elements. The terminal operation is to actually collect them in a Set which will ensure that our elements are distinct.

From a Map you can retrieve all the Sets using map.values(). There you an Collection of Sets.
With this code you can get all unique sets
Map<String, Set<String>> accounts = yourData;
Set<String> uniqueSet = new HashSet<String>();
foreach(Set<String> data: account.values()) {
uniqueSet.addAll(data);
}

Related

Getting multiple list of properties from a List of Objects in Java 8

Considering I have a list of objects List<Emp> where Emp has 3 properties name, id, and age. What is the fastest way to get 3 lists like List<String> names, List<String> ids, and List<Integer> ages.
The simplest I could think of is to iterate over the entire list and keep adding to these 3 lists. But, I was wondering if there is an easier way to do it with Java 8 streams?
Thanks in advance.
It's a very interesting question, however, there is no dedicated collector to handle such use case.
All you can is to use 3 iterations (Streams) respectively:
List<String> names = employees.stream().map(Emp::name).collect(Collectors.toList());
List<Integer> ids = employees.stream().map(Emp::id).collect(Collectors.toList());
List<Integer> ages = employees.stream().map(Emp::age).collect(Collectors.toList());
Edit - write the own collector: you can use the overloaded method Stream::collect(Supplier, BiConsumer, BiConsumer) to implement your own collector doing what you need:
Map<String, List<Object>> newMap = employees.stream().collect(
HashMap::new, // Supplier of the Map
(map, emp) -> { // BiConsumer accumulator
map.compute("names", remappingFunction(emp.getName()));
map.compute("ages", remappingFunction(emp.getAge()));
map.compute("ids", remappingFunction(emp.getId()));
},
(map1, map2) -> {} // BiConsumer combiner
);
Practically, all it does is extracting the wanted value (name, age...) and adding it to the List under the specific key "names", "ages" etc. using the method Map::compute that allows to compute a new value based on the existing (null by default if the key has not been used).
The remappingFunction that actually creates a new List or adds a value looks like:
private static BiFunction<String, List<Object>, List<Object>> remappingFunction(Object object) {
return (key, list) -> {
if (list == null)
list = new ArrayList<>();
list.add(object);
return list;
};
}
Java 8 Stream has some API to split the list into partition, such as:
1. Collectros.partitioningBy(..) - which create two partitions based on some Predicate and return Map<Boolean, List<>> with values;
2. Collectors.groupingBy() - which allows to group stream by some key and return resulting Map.
But, this is not really your case, since you want to put all properties of the Emp object to different Lists. I'm not sure that this can be achieved with such API, maybe with some dirty workarounds.
So, yes, the cleanest way will be to iterate through the Emp list and out all properties to the three Lists manually, as you have proposed.

How to search efficiently return all the values for keys which are in a arraylist

I am having an arraylist which contains a list of numbers. I want to get all the values from the HashMap which has the keys which are in the array list.
For example say the array list contains 1,2,3,4,5,6,7,8,9 list
I want to get all the values for the keys 1,2,3,4,5,6,7,8,9 map
So currently I am implementing
for (i=0;i<list.size;i++){
map_new.put(list.get(),map.get(list.get()))
}
Is there any efficient way to do this?
Your code basically assumes that map.get(list.get()) always returns a value, you can try the following code which first filters the not null values from the list object and then adds to the new Map:
Map<String, Integer> newMap = list.stream().
filter(key -> (map.get(key) != null)).//filter values not present in Map
collect(Collectors.toMap(t -> t, t -> map.get(t)));//now collect to a new Map
In case, if map.get(list.get()) returns null, your code creates a Map with null values in it for which you might end up doing null checks, which is not good, rather you can ensure that your newly created Map always contains a value for each key.
Assuming the signature of list and the map are as following
List<Integer> list;
Map<Integer, Integer> map;
You can use following
for(int a : list){
Integer b = map.get(a);
if(b != null)
// b is your desired value you can store in another collection
}
Which is similar to the procedure you have already used.
As you can access the map in O(1) so the complexity of this code will be O(listsize)
There is not much you can do for efficiency. Still couple of small things you can do considering code example you have given above:
1) Change your for loop to
for(Long num : list)
instead of iterating using index, this will reduce you get calls over list.
2) You can update the existing map , so that you even do not need to iterate.
map.keySet().retainAll(list);
for(Long key: map.keySet()) {
System.out.println(map.get(key));
}
With this existing map will contain only those data whose keys are present in list, but you should use it carefully depending upon rest of the code logic.
You can capitalize on the fact that the keyset of a map is backed by the map itself and modifications to the keyset will reflect back to the map itself. This way, you can use the retainAll() method of the Set interface to reduce the map with a single line of code. Here is an example:
final Map<Integer, String> m = new HashMap<Integer, String>();
m.put(1, "A");
m.put(2, "B");
m.put(3, "C");
m.put(4, "D");
m.put(5, "E");
final List<Integer> al = Arrays.asList(new Integer[] { 2, 4, 5 });
System.out.println(m);
m.keySet().retainAll(al);
System.out.println(m);
This will output:
{1=A, 2=B, 3=C, 4=D, 5=E}
{2=B, 4=D, 5=E}

Extract any value from hashmap (one for each key)

I have a large map with different keys and several values (DepthFeed) associated to each. I would like to get any value (DepthFeed) from that to be able to extract the name of the instrument one for each key.
I have this map
private static Map<Integer, List<DepthFeed>> mapDepthFeed = new HashMap<>();
From that I would like to do something like, however not returning the keyset integer. Instead I want a List<DepthFeed> back (containing one row for each key)
List<DepthFeed> d = mapPriceFeed.values().stream().distinct().collect(Collectors.toList());
Use
List<DepthFeed> result = mapDepthFeed.values().stream()
.filter(list -> !list.isEmpty())
.map(list -> list.get(0))
.collect(Collectors.toList());
This way you will get the first element from each non-empty list stored in map values.

Converting a Map to a List

Is there anyone who knows how to convert a Map to a List
I found here something like this :
List<Value> list = new ArrayList<Value>(map.values());
But this is going to store just the value of the Map to the List
What I want to do is : copy the Key & the Value to the List
So, do you know how to achieve this ?
This will give you a List of the Map entries:
List<Map.Entry<Key, Value>> list =
new ArrayList<Map.Entry<Key, Value>>(map.entrySet());
FYI, entries have a getKey() and a getValue() method.
One way you can do is Create a List with adding all the keys like :
List list = new ArrayList(map.keySet());
Then add all the values like :
list.addAll(map.values);
And then probably you have to access with index like:
if map size is 10 , you know that you have 20 elements in the list.
So you have to write a logic to access the key-value from the list with proper calculation of index like: size/2 something like that.
I am not sure if that helps what your requirement is.
Both #Bohemian and #dacwe are right. I'd say moreover: in most cases you do not have to create your own list. Just use map.entrySet(). It returns Set, but Set is just a Collection that allows iterating over its elements. Iterating is enough in 95% of cases.
Try storing the Map.Entrys of the map:
new ArrayList<Entry<Key, Value>>(map.entrySet());
Example:
public static void main(String[] args) {
Map<String, Integer> map = new HashMap<String, Integer>();
map.put("Hello", 0);
map.put("World!", 1);
ArrayList<Entry<String, Integer>> list =
new ArrayList<Entry<String, Integer>>(map.entrySet());
System.out.println(list.get(0).getKey() + " -> " + list.get(0).getValue());
}

Get 3 highest values from Map<String,String>

I have a Map<String,String> which has entries like "User1","43". Now I want a "Top 3" of the highest values.
It would be easier with a Map<String,Integer>, but due to technical limitations I can just grab the Map as a <String,String>.
What's the most efficient way to convert a <String,String> map to a <String,Int> one and then sort it?
To convert from <String, String> to <String, Integer> you can use:
Map<String, Integer> treemap = new HashMap<String, Integer>();
for (Entry<String, String> entry : entries) {
treemap.put(entry.getKey(), Integer.parseInt(entry.getValue()));
}
However, then you will have to iterate the Map again. If you don't need the whole map, but rather just the top 3, then you can simply iterate the entries and get the top three by comparison.
Or you can reverse the key and value and use a TreeMap<Integer, String> with a Comparator, if you need both the top elements and the whole data.
There are a few ways:
Create SortedMap, e.g. TreeMap with a custom -anonymous- Comparator which performs comparisons by looking up the keys it gets in the compare() method call against the values in the original map.
Populate it with all key/value entries in the original through addAll() method.
Watch the map being sorted by value.
Grab the head/tail (depending on how your comparator sorts)
Similar to above:
Create a TreeSet of keys with a custom comparator as above...
Populate it with the keySet() of your original map.
Grab the head/tail set of the keys.
Create a new Map from those keys and value from the original map...
You could just put the values in a List and sort it:
ArrayList<Integer> highest = new ArrayList<Integer>();
for (String value : map.values()) {
highest.add(Integer.parseInt(value));
}
Collections.sort(highest);
for(int i = highest.size() - 1; i >=0 && i > highest.size()-4; i--){
System.out.println(highest.get(i));
}
If the map is very large it might be better to iterate through it and only select the 3 highest values without sorting the whole list.
You could iterate through the values of the Map (with Map.values()), converting each to an Integer (with Integer.getInteger(String s)), and keeping track of the top 3 you see.
Or, you could do as above but instead of keeping track of the top 3, make a LinkedList and insert each Integer at the correct place (traverse the LinkedList until you find where the Integer should be inserted).

Categories