I have a map of some values like this:
Map<String, String> roleMap = new HashMap<>();
map.put(1,"A");
map.put(2,"B;C;D");
map.put(3,"A;C");
And the list of some restricted values:
List<String> restrictedList= List.of("B", "C");
I need to check if the value "A"
is presented in each key of the map and not together with one of the values from restricted list, then throw some error.
I succeeded to do this but think it can be done better with streams fully and not a half job.
This is my solution:
map.forEach((key, value) -> {
if (Arrays.stream(value.toString().split(";"))
.anyMatch(role -> "A".contains(role))
&& CollectionUtils.containsAny(Arrays.asList(value.toString().split(";")), restrictedList)) {
errorsList.add(String.format("%s combination can't be used with 'A'", value))
};
});
The result of above scenario should output that key number 3
is invalid because only key number 3
contains "A
and also some value from restricted list.
But I wonder how to implement it with streams?
I tried with filter()
before anyMatch()
but it doesn't work.
CodePudding user response:
You can create a stream over the values of the map and apply filter()
operation on it twice:
- to find out if the target string (
"A"
) in your code is contained in the map value; - to determine whether there's an intersection between a particular map value and the given restricted list.
Both checks are making of anyMatch()
operation.
In order to improve performance of checking against the restricted lists, it's being wrapped with a HashSet
.
A string array is being created via split()
operation from each value only once to reduce the overheads.
Target value ("A"
), error message and delimiter (";"
) are being passed to the method dynamically and not hard-coded.
The code might look like this:
public static List<String> getErrorList(List<String> restricted,
Map<String, String> map,
String targetValue, // in your code "A"
String errorMessage,
String delimiter) {
Set<String> restrictedSet = new HashSet<>(restricted); // list is being wrapped by the set to improve performance
return map.values().stream() // Stream<String>
.map(str -> str.split(delimiter)) // Stream<String[]>
.filter(arr -> Arrays.stream(arr).anyMatch(targetValue::equals)) // contain the target value
.filter(arr -> Arrays.stream(arr).anyMatch(restrictedSet::contains)) // contains any of the restricted values
.map(arr -> String.join(delimiter, arr)) // Stream<String>
.map(str -> String.format("%s %s %s", str, errorMessage, targetValue))
.collect(Collectors.toList());
}
main()
- demo
public static void main(String[] args) {
Map<String, String> roleMap =
Map.of("1","A", "2","B;C;D", "3","A;C");
List<String> restrictedValues = List.of("B", "C");
String errorMessage = " combination can't be used with ";
String targetValue = "A";
System.out.println(getErrorList(restrictedValues, roleMap, targetValue, errorMessage, ";"));
}
Output
[A;C combination can't be used with A]
CodePudding user response:
You could do something like below to filter the keys which have invalid values according to your requierments and collect those keys to a list/set:
Map<Integer,String> map = new HashMap<>();
map.put(1,"A");
map.put(2,"B;C;D");
map.put(3,"A;C");
List<String> restrictedList= List.of("B", "C");
List<Integer> invalidKeys = map.entrySet().stream()
.filter(e -> Arrays.asList(e.getValue().split(";")).contains("A"))
.filter(e -> Arrays.asList(e.getValue().split(";")).stream().anyMatch(restrictedList::contains))
.map(Map.Entry::getKey)
.collect(Collectors.toList());
System.out.println(invalidKeys);
If you want to avoid two filters and calling Arrays.asList
twice, you could extract a predicate like below:
Predicate<String> containsRestrictedValueAndA = s -> {
List<String> temp = Arrays.asList(s.split(";"));
return temp.contains("A") && temp.stream().anyMatch(restrictedList::contains);
};
List<Integer> invalidKeys2 = map.entrySet().stream()
.filter( e -> containsRestrictedValueAndA.test(e.getValue()))
.map(Map.Entry::getKey)
.collect(Collectors.toList());
System.out.println(invalidKeys2);
CodePudding user response:
I recommend extracting your condition checking to isInRestrictedList
method.
Then you can use stream.filter
map
in a very simple and clean way like this:
List<String> errorsList = map.entrySet()
.stream()
.filter(entry -> isInRestrictedList(entry.getValue()))
.map(entry -> String.format("key: %s, %s combination can't be used with 'A'", entry.getKey(), entry.getValue()))
.collect(Collectors.toList());
private boolean isInRestrictedList(String value) {
List<String> restrictedList = Arrays.asList("B", "C");
List<String> source = Arrays.asList(value.split(";"));
return source.contains("A") && CollectionUtils.containsAny(source, restrictedList);
}
The output of errorsList
is key: 3, A;C combination can't be used with 'A'