I have a value in a properties file that goes
currency.codes=US:USD,IN:INR,AU:AUD
I am looking to get these values into a map with a (key,value) pair like (US,USD) etc using spring el
I'm trying something like
#Value("#{'${currency.codes}'.split(',|:')}")
private Map<String, String> myMap;
This obviously doesn't work. But I would be grateful if anyone can suggest me with such minimal code or any other alternate solution.
There are a lot of properties like this that I need to get into maps.
-TIA
You can write a static helper method and use that in your expression to reduce the complexity of the SpEL code.
public class MapDecoder {
public static Map<String, String> decodeMap(String value) {
Map<String, String> map = new LinkedHashMap<>();
String[] pairs = value.split(",");
for (String pair : pairs) {
String[] parts = pair.split(":");
map.put(parts[0], parts[1]);
}
return map;
}
}
public class MyBean {
#Value("#{T(mypackage.MapDecoder).decodeMap('${currency.codes}')}")
private Map<String, String> myMap;
}
Related
The method takes in two parameters - a Map and a Set. Converts the Set to a List and starts looking for a match-a List item with a key in the Map.If a match occurs, it copies an element of the old Map to the new Map.
public Map<String, Boolean> getValidMap(Set<String> set, Map<String, Boolean> map) {
Map<String, Boolean> validMap = new HashMap<>();
List<String> mainList = new ArrayList<>(set);
for (String listRule : mainList) {
for (Map.Entry<String, Boolean> mapRule : map.entrySet()) {
if (listRule.equals(mapRule.getKey()))
validMap.put(mapRule.getKey(), mapRule.getValue());
}
}
return validMap;
}
I would like to replace the loops FOR and the IF condition with lambda expressions and streams.I am not familiar with streams and lambdas so I ask for help with this question.
Basically, you can stream the Map and then filter entries having the key in input set and finally collect those entries into Map and return it
return map.entrySet().stream()
.filter(entry->set.contains(entry.getKey())
.collect(Collectors.toMap(Map.Entry::getKey,Map.Entry::getValue));
You can just use a for directly from the Set and use computeIfPresent:
public static Map<String, Boolean> getValidMap2(Set<String> set, Map<String,
Boolean> map) {
Map<String, Boolean> validMap = new HashMap<>();
set.forEach(s -> map.computeIfPresent(s, validMap::put));
return validMap;
}
I have map of maps
Map<String, Map<String,Integer>> outerMap = new HashMap<String, Map<String, Integer>>();
and I want to put some values to inner map. Is that correct way? Or it can be done better?
class SampleMap {
Map<String, Map<String, Integer>> outerMap = new HashMap<String, Map<String, Integer>>();
public void add(String outerKey, String innerKey, Integer value) {
Map<String, Integer> tempMap = new HashMap<String, Integer>();
if (outerMap.size() > 0)
tempMap = outerMap.get(outerKey);
tempMap.put(innerKey, value);
outerMap.put(key, tempMap);
}
}
You can improve the code by avoiding the creation of a new inner map eagerly, until the point when you know that you must create it.
In addition, if you know that the inner map instance came from the outer map, you don't have to spend time putting it back where it came from.
public void add(String outerKey, String innerKey, Integer value) {
Map<String, Integer> tempMap
if (outerMap.containsKey(outerKey)) {
tempMap = outerMap.get(outerKey);
} else {
tempMap = new HashMap<String, Integer>();
outerMap.put(outerKey, tempMap);
}
tempMap.put(innerKey, value);
}
Technically there is nothing wrong in your code (except a minor improvement suggested by dasblinkenlight), but is map of maps what you really need?
If you want to read/write values by two keys, probably it's better to create map from pair of two keys (MultiKey or Pair implementation can be used) or another data structure (see this comment for details https://stackoverflow.com/a/3093993/554281)
Hi everyone this is the question
I have something like that
private Map<String, Map<String, Double>> map1 = new HashMap<String, Map<String,Double>>();
private Map<String, Map<String, Double>> map2= new HashMap<String, Map<String,Double>>();
private Map<String, Map<String, Double>> map3= new HashMap<String, Map<String,Double>>();
Map1, Map2 and Map3 are of the same type, but depending on factors the data will be agrupated in those maps.
Then I have this code to put the data on each map, acording to the discrimnatign factor
private void doSomething(data){
if(factor1){
map1.put(data);
functionForData(map1);
}
else if(factor2){
map2.put(data);
functionForData(map2);
}
else if(factor3){
map3.put(data);
functionForData(map3);
}
}
I think this isn't the better approach to handle the data and determine which map will store the information, specially because I have to repeat all the code for the functionForData() only changing the map that I need.
How can I improve this?
Thanks a lot!!
This addresses your "duplication of code" issue:
private void doSomething(data, Map<String, Map<String, Double>> map1) {
map.put(data);
functionForData(map);
}
private void doSomething(data){
if(factor1){
doSomething(map1);
}
else if(factor2){
doSomething(map2);
}
else if(factor3){
doSomething(map3);
}
}
I have a
TreeMap resMap new TreeMap<String, Map<String, String>>();
I would like to filter and keep only entries that values contains a known pair, let's say ('mike' => 'jordan'), and avoid a loop like below
Is there in my included libraries apache.commons and google.common a filter method (that probably would do a loop too, but at least it's less verbose
for (Entry<String, TreeMap<String, String>> el : resMap.entrySet()){
if (el.getValue().get("mike").equals("jordan")){
//
}
}
You can use filters from Guava and the Predicate interface.
Predicate<T> yourFilter = new Predicate<T>() {
public boolean apply(T o) {
// your filter
}
};
So, simple example would be:
Predicate<Integer> evenFilter = new Predicate<Integer>() {
public boolean apply(Integer i) {
return (i % 2 == 0);
}
};
Map<Integer, Integer> map = new HashMap<Integer, Integer>();
Map<Integer, Integer> evenMap = Maps.filterValues(map, evenFilter);
Rather than force your client code to use a filter/loop, build what you need into the API of your class:
public class MyClass {
private TreeMap resMap new TreeMap<String, Map<String, String>>();
public void filter(String key, String value) {
// Some impl here. Either your loop or the guava approach
}
}
BTW, if you use your loop, consider changing to this:
for (Iterator<Map.Entry<String, TreeMap<String, String>>> i = resMap.entrySet().iterator(); i.hasNext();) {
Map.Entry<String, TreeMap<String, String>> entry = i.next();
if (value.equals(entry.getValue().get(key))) {
i.remove();
}
}
The changes to the loop are:
Changed order of equals to avoid NPE
Using iterator to allow removal of entries directly
Even if you don't have a class, you could easily wrap it up in a static method on a utility class, where it could also easily be parameterized to work with any nested map:
public static <K1, K2, V> void filter(Map<K1, Map<K2, V>> map, K2 key, V value) {
// Some impl here
}
Here's a non-guava impl for the static method:
for (Iterator<Map.Entry<K1, Map<K2, V>>> i = map.entrySet().iterator(); i.hasNext();) {
Map.Entry<K1, Map<K2, V>> entry = i.next();
if (value.equals(entry.getValue().get(key))) {
i.remove();
}
}
From #Ferrybig answer in this post.
You can use the Java 8 method Collection.removeIf for this purpose:
map.values().removeIf(Object o -> o.get("mike").equals("jordan"));
This removed all values that match the predicate.
Online demo
This works by the fact that calling .values() for a HashMap returns a collection that delegated modifications back to the HashMap itself, meaning that our call for removeIf() actually changes the HashMap (this doesn't work on all java Map's)
Take a look at Guava's Predicates and Functions.
Here are two examples. The both print the key based on match in the value's properties.
private static void printMatchingEntriesUsingALoop(Map<String, Map<String, String>> resMap, String key, String value) {
for (Map.Entry<String, Map<String, String>> entry : resMap.entrySet())
if (value.equals(entry.getValue().get(key)))
System.out.println(entry.getKey());
}
private static void printMatchingEntriesUsingGuava(Map<String, Map<String, String>> resMap, final String key, final String value) {
Predicate<Map<String, String>> keyValueMatch =
new Predicate<Map<String, String>>() {
#Override
public boolean apply(#Nullable Map<String, String> stringStringMap) {
return value.equals(stringStringMap.get(key));
}
};
Maps.EntryTransformer<String, Map<String, String>, Void> printKeys =
new Maps.EntryTransformer<String, Map<String, String>, Void>() {
#Override
public Void transformEntry(#Nullable String s,
#Nullable Map<String, String> stringStringMap) {
System.out.println(s);
return null;
}
};
Maps.transformEntries(Maps.filterValues(resMap, keyValueMatch), printKeys);
}
public static void main(String... args) {
Map<String, Map<String, String>> resMap = new TreeMap<String, Map<String, String>>();
printMatchingEntriesUsingALoop(resMap, "first", "mike");
printMatchingEntriesUsingGuava(resMap, "first", "mike");
}
One uses a loop and one use Guava.
While the first one performs better, you should really decide which will be the easiest to understand and maintain.
Some suggestions from #missingfaktor. You have to use your own judgement, but he highlighted some of the issues well.
a lot of code duplication.
special case handling.
More cyclomatic complexity.
More chances of error, as a result of first three bullets.
Hard to follow code.
Imagine you are a new developer who has to support this software. Which would you rather be facing?
You can filter the map using java 8 and streams. The first step in this process is converting to a stream using entrySet().stream(). This gives you a Stream<Map.Entry<String, TreeMap<String, String>>. You can then use filter(...) to filter the list. When you filter, you should return true when the incoming value should be included in the filter result. After you filtered the results, you can use foreach to loop over the final result.
The final result will look like the following:
resMap.entrySet().stream()
.filter(e -> el.getValue().get("mike").equals("jordan"))
.foreach(e -> {
// Do something with your entry here
});
Given the class:
public class CategoryValuePair
{
String category;
String value;
}
And a method:
public Map<String,List<String>> convert(CategoryValuePair[] values);
Given that in values we can receive many entries with the same category, I want to convert these into a Map grouped on category.
Is there a quick / efficient way to perform this conversion?
As far as I know there is not easier way than iterating on values, and then putting the values in the map (like some predefined method).
Map<String, List<String>> map = new HashMap<String, List<String>>();
if (values != null) {
for (CategoryValuePair cvp : values) {
List<String> vals = map.get(cvp.category);
if (vals == null) {
vals = new ArrayList<String>();
map.put(cvp.category, vals);
}
vals.add(cvp.value);
}
}
I changed the map values from String[] to List<String> since it seems easier to me to use that so you don't have to hassle with array resizing.
To make it in fewer lines of code, use Google Collections:
public Map<String, Collection<String>> convert(CategoryValuePair[] values) {
Multimap<String, String> mmap = ArrayListMultimap.create();
for (CategoryValuePair value : values) {
mmap.put(value.category, value.value);
}
return mmap.asMap();
}
If you don't want to allow duplicate values, replace ArrayListMultimap with HashMultimap.
With lambdaj you just need one line of code to achieve that result as it follows:
group(values, by(on(CategoryValuePair.class).getCategory()));
Just for the sake of implementation... The method returns Map and also checks for duplicates in the arrays... though performance wise its heavy ...
public Map<String,String[]> convert(CategoryValuePair[] values)
{
Map<String, String[]> map = new HashMap<String, String[]>();
for (int i = 0; i < values.length; i++) {
if(map.containsKey(values[i].category)){
Set<String> set = new HashSet<String>(Arrays.asList(map.get(values[i].category)));
set.add(values[i].value);
map.put(values[i].category, set.toArray(new String[set.size()]));
}else {
map.put(values[i].category, new String[]{values[i].value});
}
}
return map;
}