Java Using Multiple predicate for anyMatch - java

I am having a List of HashMap i.e List<Map<String, Object>> result. I want to check if some key value is present on this list or not.
For Example:
Name Count
Marvel 10
DC 0
I want that my list of map should contain at least one entry for the above table for that I am using anyMatch
Assert.assertTrue(
result.stream().anyMatch(
ag -> ( "Marvel".equals(ag.get("Name")) && 10==(int)ag.get("Count"))
));
How can I use multiple Predicates ? I can have a list of 1000 HashMap, I just want to check if any two Hash-map from my list contains those two entries.

Your data structure seems wrong for a start. A List<Map<String, Object>>, where each map represents a row and has only one key and one value? You just need a Map<String, Integer>. The string is the name, the integer is the count. Remember to give it a meaningful name, e.g. comicUniverseToSuperheroFrequency.
With that said, Predicate has an and method which you can use to chain conditions together. It might look something like this:
public static void main(String[] args)
{
Map<String, Integer> comicUniverseToSuperheroFrequency = /*something*/;
boolean isMarvelCountTen = comicUniverseToSuperheroFrequency.entrySet().stream()
.anyMatch(row -> isMarvel().and(isTen()).test(row));
}
private static Predicate<Map.Entry<String, Integer>> isMarvel()
{
return row -> "Marvel".equals(row.getKey());
}
private static Predicate<Map.Entry<String, Integer>> isTen()
{
return row -> row.getValue() == 10;
}

Related

Stream API how to modify key and value in a map?

I have a String - Array map that looks like this
dishIdQuantityMap[43]=[Ljava.lang.String;#301d55ce
dishIdQuantityMap[42]=[Ljava.lang.String;#72cb31c2
dishIdQuantityMap[41]=[Ljava.lang.String;#1670799
dishIdQuantityMap[40]=[Ljava.lang.String;#a5b3d21
What I need to do is
Create a new map, where key - only numbers extracted from String like this ( key -> key.replaceAll("\\D+","");
Value - first value from array like this value -> value[0];
Filter an array so that only this paris left where value > 0
I've spent an hour trying to solve it myself, but fail with .collect(Collectors.toMap()); method.
UPD:
The code I've done so far. I fail to filter the map.
HashMap<Long, Integer> myHashMap = request.getParameterMap().entrySet().stream()
.filter(e -> Integer.parseInt(e.getValue()[0]) > 0)
.collect(Collectors.toMap(MapEntry::getKey, MapEntry::getValue));
You can do it by using stream and an auxiliary KeyValuePair class.
The KeyValuePair would be as simple as:
public class KeyValuePair {
public KeyValuePair(String key, int value) {
this.key = key;
this.value = value;
}
private String key;
private int value;
//getters and setters
}
Having this class you can use streams as bellow:
Map<String, Integer> resultMap = map.entrySet().stream()
.map(entry -> new KeyValuePair(entry.getKey().replaceAll("Key", "k"), entry.getValue()[0]))
.filter(kvp -> kvp.value > 0)
.collect(Collectors.toMap(KeyValuePair::getKey, KeyValuePair::getValue));
In the example I'm not replacing and filtering exactly by the conditions you need but, as you said you are having problems with the collector, you probably just have to adapt the code you currently have.
I addressed this problem in the following order:
filter out entries with value[0] > 0. This step is the last on your list, but with regards to performance, it's better to put this operation at the beginning of the pipeline. It might decrease the number of objects that have to be created during the execution of the map() operation;
update the entries. I.e. replace every entry with a new one. Note, this step doesn't require creating a custom class to represent a key-value pair, AbstractMap.SimpleEntry has been with us for a while. And since Java 9 instead of instantiating AbstractMap.SimpleEntry we can make use of the static method entry() of the Map interface;
collect entries into the map.
public static Map<Long, Integer> processMap(Map<String, String[]> source) {
return source.entrySet().stream()
.filter(entry -> Integer.parseInt(entry.getValue()[0]) > 0)
.map(entry -> updateEntry(entry))
.collect(Collectors.toMap(Map.Entry::getKey,
Map.Entry::getValue));
}
private static Map.Entry<Long, Integer> updateEntry(Map.Entry<String, String[]> entry) {
return Map.entry(parseKey(entry.getKey()), parseValue(entry.getValue()));
}
The logic for parsing keys and values was extracted into separate methods to make the code cleaner.
private static Long parseKey(String key) {
return Long.parseLong(key.replaceAll("\\D+",""));
}
private static Integer parseValue(String[] value) {
return Integer.parseInt(value[0]);
}
public static void main(String[] args) {
System.out.println(processMap(Map.of("48i;", new String[]{"1", "2", "3"},
"129!;", new String[]{"9", "5", "9"})));
}
Output
{48=1, 129=9}

Is there a way to concatenate grouped lists into a set in Java 8 in one line?

I'm in a weird situation where have a JSON API that takes an array with strings of neighborhoods as keys and an array of strings of restaurants as values which get GSON-parsed into the Restaurant object (defined with a String for the neighborhood and a List<String> with the restaurants). The system stores that data in a map whose keys are the neighborhood names and values are a set of restaurant names in that neighborhood. Therefore, I want to implement a function that takes the input from the API, groups the values by neighborhood and concatenates the lists of restaurants.
Being constrained by Java 8, I can't use more recent constructs such as flatMapping to do everything in one line and the best solution I've found is this one, which uses an intermediate map to store a Set of List before concatenating those lists into a Set to be store as value in the final map:
public Map<String, Set<String>> parseApiEntriesIntoMap(List<Restaurant> restaurants) {
if(restaurants == null) {
return null;
}
Map<String, Set<String>> restaurantListByNeighborhood = new HashMap<>();
// Here we group by neighborhood and concatenate the list of restaurants into a set
Map<String, Set<List<String>>> map =
restaurants.stream().collect(groupingBy(Restaurant::getNeighborhood,
Collectors.mapping(Restaurant::getRestaurantList, toSet())));
map.forEach((n,r) -> restaurantListByNeighborhood.put(n, Sets.newHashSet(Iterables.concat(r))));
return restaurantListByNeighborhood;
}
I feel like there has to be a way do get rid of that intermediate map and do everything in one line...does someone have a better solution that would allow me to do this?
You could with Java-8 simply use toMap with a mergeFunction defined as:
public Map<String, Set<String>> parseApiEntriesIntoMap(List<Restaurant> restaurants) {
// read below about the null check
return restaurants.stream()
.collect(Collectors.toMap(Restaurant::getNeighborhood,
r -> new HashSet<>(r.getRestaurantList()), (set1, set2) -> {
set1.addAll(set2);
return set1;
}));
}
Apart from which, one should ensure that the check and the result from the first block of code from your method
if(restaurants == null) {
return null;
}
when on the other hand dealing with empty Collections and Map, it should be redundant as the above code would return empty Map for an empty List by the nature of stream and collect operation itself.
Note: Further, if you may require a much relatable code to flatMapping in your future upgrades, you can use the implementation provided in this answer.
Or a solution without using streams, in this case, would look similar to the approach using Map.merge. It would use a similar BiFunction as:
public Map<String, Set<String>> parseApiEntriesIntoMap(List<Restaurant> restaurants) {
Map<String, Set<String>> restaurantListByNeighborhood = new HashMap<>();
for (Restaurant restaurant : restaurants) {
restaurantListByNeighborhood.merge(restaurant.getNeighborhood(),
new HashSet<>(restaurant.getRestaurantList()),
(strings, strings2) -> {
strings.addAll(strings2);
return strings;
});
}
return restaurantListByNeighborhood;
}
You can also flatten the Set<List<String>> after collecting them using Collectors.collectingAndThen
Map<String, Set<String>> res1 = list.stream()
.collect(Collectors.groupingBy(Restaurant::getNeighborhood,
Collectors.mapping(Restaurant::getRestaurantList,
Collectors.collectingAndThen(Collectors.toSet(),
set->set.stream().flatMap(List::stream).collect(Collectors.toSet())))));

While converting a list of String to map using toMap() i am getting illigleStateExcption why?

Below is the sample code, Suggest me what is wrong here as per my knowledge, I am passing a new key every time still it says duplicate key.
public class CollectorsDemo5 {
public static void main(String[] args) {
List<String> listOfCities=new ArrayList<String>();
listOfCities.add("Istanbul");
listOfCities.add("Istanbul");
listOfCities.add("Budapest");
listOfCities.add("Delhi");
listOfCities.add("Amsterdam");
listOfCities.add("Canberra");
listOfCities.add("Canberra");
Map<Integer, String> map = listOfCities.stream()
.collect(Collectors.toMap(listOfCities::indexOf, Function.identity()));
map.forEach((k,v)->System.out.println("key:"+k +" value:"+v));
}
}
Some of list elements are duplicate.
Hence indexOf will return the same value -> Throw duplicate key exception.
What you can do is to do a normal for loop:
Map<Integer, String> map = new HashMap<>();
for (int i = 0; i < listOfCities.size(); i++) {
map.put(i, listOfCities.get(i));
}
You get that exception because you have duplicate values in your stream. You can produce the result you desire with IntStream:
Map<Integer, String> map = IntStream.range(0, listOfCities.size())
.mapToObj(Function.identity())
.collect(Collectors.toMap(Function.identity(), listOfCities::get));
Using a simple for loop would be the way to go here. If you really want to use Streams
IntStream.range(0, listOfCities.size())
.collect(HashMap::new, (map, index) -> map.put(index, listOfCities.get(index)), HashMap::putAll);
This produces
{0=Istanbul, 1=Istanbul, 2=Budapest, 3=Delhi, 4=Amsterdam, 5=Canberra, 6=Canberra}
The above creates a new HashMap to which we add the city name keyed by the index. The last combiner is only applicable when using a parallel stream and is used to combine the multiple maps.

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.

Store in a map a key with multiple values

I want to store in a map keys with multiple values.
For example : i am reading from an ArrayList the keys which are Strings and from another ArrayList the values which are integers:
Keys Values
humans 50
elfs 20
dwarfs 30
humans 40
elfs 10
and i want to store these informations like this: Map < String, ArrayList < Integer>>
[humans = {50,40}]
[elfs = {20,10}]
[dwarfs = {30}]
It is there possible to do this?
I recommend using the Guava MultiMap. Alternatively, your
Map<String, ArrayList<Integer>>
will also accomplish this. When doing a put, determine if there is already a list associated with the key; if there is then your put will be a get(key).add(value), otherwise it will be a put(new List(value)). Likewise a remove will remove a value from the associated list, or else will completely remove the list if this will result in an empty list.
Also, a Map<String, HashSet<Integer>> will probably result in better performance than a map of lists; obviously don't do this if you want to associate duplicate values with a key.
I do this:
public class StringToListInt {
private Map<String, List<Integer>> stringToListInt;
public StringToListInt() {
stringToListInt = new HashMap<String, List<Integer>>();
}
public void addInt( String string, Integer someValue ) {
List<Integer> listInt = stringToListInt.get( string );
if ( listInt == null ) {
listInt = new ArrayList<String>();
stringToListInt.put( string, listInt );
}
listInt.add( someValue );
}
public List<Integer> getInts( String string ) {
return stringToListInt.get( string );
}
}
If you add in some Generics, I imagine you would end up with something very similar to Guava's MultiMap without the dependency.

Categories