So, I have a Map (String, String) which I receive this map can have about 600-800 entries. My task is to retrieve only about 60-120 (based on some logic) of those pairs based on the keys (constant strings).
Right now I have created a List of the required Keys, I am iterating the Map I receive and pulling out required pairs into another Map and passing it on. This works, but the code looks horrible.
There surely has to be a better way of doing this. Any suggestions?
I thought of using contains instead of matching keys, but my requirement is for exact matches. Yes, I read about using RegEx for exact matches, so that can be used as a last resort.
What I need help with:
1. Better way to store required keys than having them in a List. This list of keys are going to be a constant. Having a list of 60-120 strings hardcoded into a List looks horrible.
2. Fastest (time) way to compare the said list and pull out required pairs.
My code:
List <String> keysToCheck = new ArrayList<String>();
keysToCheck.add("attrib1");
...
keysToCheck.add("attribN");
Map<String, String> newMap = new HashMap<String, String>();
for (String key : keysToCheck) {
if(mapRcvd.containsKey(key)) {
newMap.put(key, mapRcvd.get(key));
}
}
I am looking for solutions which would be quicker, as this would be a very small part of a larger application. I am good with having to write a lot of code to achieve that.
You can store all constant keys in a file, where each line contains one key. Then you can get all required keys like:
List<String> requiredKeys = Files.lines(Paths.get(PATH_TO_FILE_WITH_KEYS))
.collect(Collectors.toList());
Now get map with required keys using stream filter method:
Map<String, String> collect = mapRcvd.entrySet().stream() // get stream of map entries
.filter(entry -> requiredKeys.contains(entry.getKey())) // filter only such entries which has required key
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); // collect all filtered entries into new map
More about Collectors.toMap, javadoc
Update: while getting map with required keys may seem more compact, the for-loop with containsKey method in your example is faster than contains. You should probably use the advice #Holger left in the comments
Related
How can I transform the following data result
[a={x=0, y=43, z=57}, b={x=1, y=90, z=9}, c={x=1, y=83, z=16}]
into
{x=[0,1,1], y=[43,90,83], z=[57,9,16]}
using Map and Treemap.
Basically, I am iterating over a,b,c.. then while reading its values I want to sort them by Key(x),values(0,1,1), Key(y),values(43,90,83) and so on.
Thanks a lot for your help.
Obviously
Map<String, Map<String, Integer>> mapOfMaps = ...
TreeMap<String, List<Integer>> series = mapOfMaps.values().stream()
.map(Map::entrySet)
.flatMap(Set::stream)
.collect(Collectors.groupingBy(Entry::getKey, TreeMap::new,
Collectors.mapping(Entry::getValue, Collectors.toList())));
http://ideone.com/LHYhE7
You cannot transform data using a Map. You organize 'transformed data' into a map. Organizing the data can be done with methods exposed in String class.
It is safe to assume that the following is a String at your end.
[a={x=0, y=43, z=57}, b={x=1, y=90, z=9}, c={x=1, y=83, z=16}]
Use String API to get hold of each of the set content. http://docs.oracle.com/javase/7/docs/api/java/lang/String.html You can look at methods like indexOf or split or substring. This helps you divide the string into chunks of {.....}.
You now have to traverse and identify the integer values for x,y,z and as you find them, you can add them to a List. Example: ArrayList.
Finally, you add the string x,y,z and their corresponding ArrayLists to a TreeMap.
I am having map this way,
Map<String, Map<String, Set<String>>> sampleMap = new Map<String, Map<String, Set<String>>>();
and the data in this map would be this way,
sampleMap={2014={A=[1, 2], B=[3], 2015={A=[1,2], B=[1,2], 2016={A=[1,2], B=[3,4]}};
I want to remove the key's from the map based on this input: List<String> filter; with values this way,
filterArray : [2014, 2015]
i.e, first iterate through arraylist values one by one, verify if the arraylist value matches with any of the key in Hashmap.
if key is matched ignore it.
if key is not matched, I just want to remove that key from the map.
i.e, I always want to keep only matched keys in map, comparing with the input value passed.
In this case, as I have arraylist values this way,[2014,2015],
2014,2015 keys only to be in my map. So,
Data to be before removal:
sampleMap={2014={A=[1, 2], B=[3], 2015={A=[1,2], B=[1,2], 2016={A=[1,2], B=[3,4]}};
Data to be after removel:
sampleMap={2014={A=[1, 2], B=[3], 2015={A=[1,2], B=[1,2]}};
I tried this way, However I just want to know is this is the correct approch, or is it is prone to any of the exceptions?
Iterator<Map.Entry<String , Map<String, Set<String>>>> iter = sampleMap.entrySet().iterator();
while (iter.hasNext()) {
Map.Entry<String , Map<String, Set<String>>> entry = iter.next();
logger.info("Keys : " + entry.getKey());
if (filterArray.equalsIgnoreCase(entry.getKey())) {
iter.remove();
}
}
Use retainAll() on the keySet:
map.keySet().retainAll(list);
Seems reasonable. I might have a couple pieces of advice.
First of all, whenever I see nested collections I always wonder if there should be a class or two in there. If this is a one-time task then don't worry about it, but if you want to reuse this code you might want to think about creating a class for your inner map/set... but if it's really this simple then it's no big deal.
Secondly if you are using Java 8, using a list comprehension for filtering would perform better (Because it would automatically thread your compares) and would be cleaner. I can give you the groovy solution for what you are trying to do, but I'm not familiar enough with java 8 list comprehensions to do it correctly.
def filteredStructure=structure.findAll{entry->entry.key.equalsIgnoreCase("2014") || entry.key.equalsIgnoreCase("2015"))
The java version should be really similar.
I am trying to adapt Lambda features, however few struggles here and there.
List<Map<String, String>> list = new LinkedList<>();
Map<String, String> map = new HashMap<>();
map.put("data1", "12345");
map.put("data2", "45678");
list.add(map);
I just want to print the values in comma separated format like 12345,45678
So here goes my trial
list.stream().map(Map::values).collect(Collectors.toList()) //Collectors.joining(",")
and the output is [[12345,45678]]. It means, there's a list and inside list it's creating the comma separated value at 0 index. I do understand why it's doing though.
But I didn't get through how to extract my desired result unless I call .get(0) in the end of that expression.
Any help/some more insights on how to use lambdas better will be helpful
Try this :
list.stream()
.map(Map::values)
.flatMap(Collection::stream)
.collect(Collectors.joining(","))
The flatMap method flattens out a Collection<Stream<String>> into a single Stream<String>.
Alternatively, you can try what #Holger suggested in the comments.
Note that since you are using a Map, there is no guarantee that the order will be preserved. You might see the output as 45678,12345 at your end. If you wish to preserve the order, you can use a LinkedHashMap instead of a HashMap.
In Mysql we can query a table having clause " WHERE name LIKE '%someName%' ", can we have the same functionality with HashMap in java,if so how can we achieve this more efficiently in a less time by not iterating over each element?
If you're using Java SE 8 and the new Streams API: there is a filter method which basically is what you are looking for, I think.
e.g. something like (untested!):
myMap.entrySet().stream().filter(entry -> entry.getKey().contains("someName")).map(entry -> entry.getValue()).collect(Collectors.toList());
You can iterate on all your keys and check if they match a regexp. This might not be the most efficient way of doing it, but it's the first thing I thought of.
Here is what it would look like:
Pattern p = Pattern.compile("*someName*"); // the regexp you want to match
List<String> matchingKeys = new ArrayList<>();
for (String key : map.keySet()) {
if(p.matcher(key).matches()) {
matchingKeys.add(key);
}
}
// matchingKeys now contains the keys that match the regexp
Note: map is supposed to be declared earlier like this:
HashMap<String, SomeValueClass> map = new HashMap<>();
Remove all values which doesn't contain key part:
yourMap.keySet().removeIf(key -> !key.contains(keyPart));
Or regex:
yourMap.keySet().removeIf(key -> !key.matches(".*keyPart.*"));
There may be a way to achieve this functionality in Java, but HashMap will not do it.
If you want to get all the values in a map that are associated to a given key you are doing a reverse lookup.
You would need to iterate over every key / value pair in the map, searching for the given value and storing it into a collection. Something like this:
List<KeyType> keys = new ArrayList<>();
for (Map.Entry<KeyType, ValueType> e : myMap)
if(e.getValue().equals(valueWeAreSearchingFor)) keys.add(e.getKey());
I have a text file which looks like this:
code appearance
----------------
j4t8 1
fj89 3
pf6n 1
j4t8 5
And I want to sort by the codes which appear the most. As you can see (and since I want to perform a group by) there are duplicate codes, so using HashMap would be a problem (duplicate keys). Any ideas?
don't know if this is the best solution but you could create a map of a list like this:
Map<String, List<Integer>> map = new HahsMap<String, List<Integer>>();
if(map.contains.(key))
{
map.get(key).add(new_appearance_value);
}
else
{
List<Integer> app = new ArrayList<Integer>();
app.add(new_appearance_value);
map.put(key, app);
}
Where the map key would be the code and the values of appearance would go into the list.
Note: to determine which code has more appearances just check for the size of the list of each code.
You can use
HashMap map = new HashMap<String, List<Integer>>();
The appearances will be stored in a list associated with every code.
Then given a code you just retrieve the list of integers and iterate over it.
You need a Collection of Pair objects. Each pair holds the code and the appearance. You then sort the collection using a Comparator, which only compares the appearance in each Pair object, and disregards the code.
The Commons Collections MultiValueMap can be used to decorate another map, allowing it to have more than one value for a key.