Matching two MultivaluedMaps and retrieving values - java

MultivaluedMap<String, Pair<String, Float>> dataToCheck;
MultivaluedMap<String, String> inputData;
I need to match the key of dataToCheck with inputData.
Followed by matching the values of dataToCheck with inputData.
If the values match, I retrieve the Float value. I want to exit at the first match.
Can exit with one key match.
float compareMap(MultivaluedMap<String, Pair<String, Float>> dataToCheck, MultivaluedMap<String, String> inputData) { }
EDIT:
MultivaluedMap is from Jax-Rs (https://docs.oracle.com/javaee/7/api/javax/ws/rs/core/MultivaluedMap.html)
This is what I came up with. I know I can use filter, but I am not sure how to make it work.
float sampleValue = new float[1];
dataToCheck.keySet().stream().forEach(field -> {
List<String> inputValues = inputData.get(field);
List<Pair<String, Float>> checkValues = dataToCheck.get(field);
if (checkValues != null && inputValues != null) {
checkValues.stream().forEach(value -> {
for (String inp : inputValues) {
if (value.getLeft().equalsIgnoreCase(inp)) {
sampleValue[0] = value.getRight();
break;
}
}
});
}
});

A slightly cleaner way of performing that would be:
private static Float findFirstMatchingRight(MultivaluedMap<String, Pair<String, Float>> dataToCheck,
MultivaluedMap<String, String> inputData) {
for (Map.Entry<String, List<Pair<String, Float>>> entry : dataToCheck.entrySet()) {
Optional<Float> sampleValue = entry.getValue().stream()
.filter(cv -> inputData.get(entry.getKey())
.stream()
.anyMatch(inp -> cv.getLeft().equalsIgnoreCase(inp)))
.findFirst()
.map(Pair::getRight);
if (sampleValue.isPresent()) {
return sampleValue.get();
}
}
return Float.NaN; // when the condition doesn't match anywhere in the maps
}

Related

finding a specific value in a hashmap

Is there a code for finding a specific value in a hashmap?
I want to use a for loop to convert values in a hashmap into an int.
for (int i = 0; i < items; i++) {
cost = Integer.parseInt(myHashmap);
}
can I even use .parseInt on a hashmap or is there another way to convert a place in a hashmap into a int?
Like String[3] is there a code to find a specific place in a hashmap?
To iterate over all values of a map, use the values method:
Map<Long, String> map = ...;
for (final String value = map.values()) {
System.out.println(value);
}
To find a specific value, iterate all values, check against your predicate and return if found:
String findValue(final Map<Long, String> map, final Predicate<String> condition) {
for (final String value = map.values()) {
if (condition.test(value)) {
return value;
}
}
return null;
}
To find the key for a given value, iterate the entry set of the map:
Long findKey(final Map<Long, String> map, final String value) {
for (final Map.Entry<Long, String> entry = map.entrySet()) {
if (Objects.equals(entry.getValue(), value)) {
return entry.getKey();
}
}
return null;
}
Of course, once you have a value (or a key), you can use it any way you like. That includes passing it as argument to Integer.parseInt.
myHashmap.values() will return all the values of the Map. Integer.parseInt(value) parses the String argument as a signed decimal integer object.
public static void main(String[] args) {
Map<String, String> myHashmap = new HashMap<>();
myHashmap.put("A", "10");
myHashmap.put("B", "20");
myHashmap.put("C", "30");
myHashmap.values().forEach(value -> {
System.out.println(Integer.parseInt(value));
// Rest of the logic
});
}

How to remove null or empty list from an "unmodifiable" map

I have an unmodifiable map (Map<String, Object>). This contains a key-value pair, where the value should be a List, but for a specific key the value is an empty List.
How can I remove that empty list from the map and return?
I have done it using predicate which checks if the value is an instance of collection and then check if Collections.isNotEmpty(..).
I want to know is there any better approach than this? (In Java 8)
public class StudentTest {
Predicate<Object> isCollectionNotEmpty = input -> {
if (input instanceof Collection) {
return CommonCollectionUtils.isNotEmpty((Collection<?>)input);
}
return true;
};
Predicate<Object> isObjectNotNull = input -> Objects.nonNull(input);
public static void main(String[] args) {
StudentTest s = new StudentTest();
final Map<String, Object> map1 = new HashMap<>();
map1.put("student_batch_size", 200);
map1.put("student_avg_height", 172);
map1.put("Student_names", Collections.emptyList());
final Map<String, Object> finalMap = s.getFinalMap(Collections.unmodifiableMap(map1));
System.out.println(finalMap);
}
private Map<String, Object> getFinalMap(final Map<String, Object> inputMap) {
final Map<String, Object> resultMap = new HashMap<>();
//inputMap.values().remove(Collections.emptyList());
resultMap.putAll(inputMap.entrySet().stream()
.filter(entry -> isObjectNotNull.and(isCollectionNotEmpty).test(entry.getValue()))
.collect(Collectors.toMap(e -> e.getKey(), e -> e.getValue())));
return resultMap;
}
}
Expected Output:
{student_avg_height=172, student_batch_size=200}
Collection#removeIf might be a good option to consider.
private Map<String, Object> getFinalMap(final Map<String, Object> inputMap) {
final Map<String, Object> resultMap = new HashMap<>(inputMap);
resultMap.entrySet().removeIf(e -> e.getValue() instanceof Collection && ((Collection) e.getValue()).isEmpty());
return resultMap;
}
If a stream-based approach is more appealing to you
private Map<String, Object> getFinalMap(final Map<String, Object> inputMap) {
return inputMap.entrySet()
.stream()
.filter(e -> !(e.getValue() instanceof Collection && ((Collection) e.getValue()).isEmpty()))
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
}
UPDATE
If you want to filter out null objects regardless of their type, here are slight changes to the methods mentioned above
1) removeIf
resultMap.entrySet()
.removeIf(e -> Objects.isNull(e.getValue()) ||
(e.getValue() instanceof Collection && ((Collection) e.getValue()).isEmpty());
2) stream-based
.filter(e -> Objects.nonNull(e.getValue()))
.filter(e -> !(e.getValue() instanceof Collection && ((Collection) e.getValue()).isEmpty()))
I think your approach of using Object as a value in the Map is questionable. You don't fully control type safety. I'd consider replacing the student Map with an object reflecting single student batch properties.
class StudentBatch {
int size;
int avgHeight;
Collection<String> names;
boolean hasValidNames() {
return names != null && !names.isEmpty();
}
}
As you see I've put the hasValidNames predicate to the object which makes it reusable and allows to simplify the condition you're trying to optimize.
List<StudentBatch> batchList = new ArrayList<>();
batchList.stream().filter(StudentBatch::hasValidNames).collect(Collectors.toList());
instanceof checks the no nullity too, so no need to make it explicitly and you don't need to create a new HashMap even if the Map that you stream is not modifiable as the stream doesn't modify it directly but creates a new Map :
final Map<String, Object> finalMap =
map
.entry()
.stream()
.filter(e-> !isEmptyCollection(e))
.collect(toMap(Entry::getKey, Entry::getValue);
public void boolean isEmptyCollection(Entry<String, Object> entry){
return entry.getValue() instanceof Collection && ((Collection<?>) entry.getValue()).isEmpty();
}

flatten Map in Map structure with stream [duplicate]

This question already has answers here:
How does recursion work with Java 8 Stream?
(2 answers)
Closed 4 years ago.
I have the following structure in HashMaps (not all the values are maps (see id) and there are more levels embedded):
{
"id":"0",
"name":{
"title":"Mr.",
"personName": {
"firstName":"Boot",
"familyName":"Strap"
}
}
}
Can I flatten it with Java 8 stream like
{
"id":"0",
"name.title":"Mr.",
"name.personName.firstName":"Boot",
"name.personName.familyName":"Strap"
}
?
Streams does not do recursive computation, you may use classic way like
static Map<String, String> flatten(Map<String, Object> map, String prefix) {
prefix = prefix.isEmpty() ? prefix : prefix + ".";
Map<String, String> res = new HashMap<>();
for (Map.Entry<String, Object> entry : map.entrySet()) {
if (entry.getValue() instanceof Map) {
res.putAll(flatten((Map) entry.getValue(), prefix + entry.getKey()));
} else {
res.put(prefix + entry.getKey(), entry.getValue().toString());
}
}
return res;
}
Stream way will look like
static Map<String, String> flatten(Map<String, Object> map, String p) {
final String prefix = p.isEmpty() ? p : p + ".";
return map.entrySet()
.stream()
.map(e -> {
if (e.getValue() instanceof Map) {
return (Set<Map.Entry<String, String>>) flatten((Map) e.getValue(), prefix + e.getKey()).entrySet();
} else {
return Map.of(prefix + e.getKey(), e.getValue().toString()).entrySet();
}
})
.flatMap(Set::stream)
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
}
And use as
public static void main(String[] args) {
Map<String, Object> map = Map.of("id", 0, "name", Map.of("title", "Mr.", "personName", Map.of("firstName", "Boot", "familyName", "Strap")));
Map<String, String> res = flatten(map, "");
res.entrySet().forEach(System.out::println);
}
name.title=Mr.
name.personName.familyName=Strap
name.personName.firstName=Boot
id=0
It is pretty simple not using Streams, but use recursion:
public static Map<String, String> flatten(Map<String, Object> map) {
return flatten("", map, new LinkedHashMap<>());
}
private static Map<String, String> flatten(String parent, Map<String, Object> map, Map<String, String> res) {
for (Map.Entry<String, Object> entry : map.entrySet()) {
String key = parent.isEmpty() ? entry.getKey() : parent + '.' + entry.getKey();
if (entry.getValue() instanceof Map)
flatten(key, (Map<String, Object>)entry.getValue(), res);
else
res.put(key, String.valueOf(entry.getValue()));
}
return res;
}
P.S. I know that string concatenation in loop is not good (it is better to use StringBuilder or even Deque), but I use String to make this example more clear.

How to filter a key in a Map with only 3 string length?

I have the LinkHashMap here.
Map<String, String> map
{011A=HongKong, 012B=London, 015Y=NewYork, 312=China, 272=Canada}
I would like to filter or arrange the map to be this,
Only the key with a 3 digit or length = 3 kept in the map.
{312=China, 272=Canada}
What kind of method could I use?
Thank you very much!
You can use the Iterator
Iterator<String> it = map.keySet().iterator();
while (it.hasNext())
{
String s = it.next();
if(s.length() != 3){
it.remove();
}
}
If you are using Java 8 (or higher) there is a convenient feature called Lambda that provides a nice api to work with streams.
To create a second map with only the filtered keys use this below code:
Map<String, String> originMap;
Map<String, String> filteredmap = originMap.entrySet().stream()
.filter(x -> x.getKey().length() == 3)
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
If you want to remove the elements from your map itself :
Map<String, String> map;
map.entrySet().removeIf(entry -> entry.getKey().length != 3);
You can write a method like this to filter,
import java.util.HashMap;
import java.util.Map;
public class Main {
public static void main(String[] args) {
Map<String, String> map = new HashMap<String, String>() {{
put("011A", "HongKong");
put("012B", "London");
put("015Y", "NewYork");
put("312", "China");
put("272", "Canada");
}};
Map<String, String> filteredMap = filterMap(map);
}
static Map<String, String> filterMap (Map<String, String> map) {
HashMap<String, String> filteredMap = new HashMap<>();
for (String key: map.keySet()) {
if (key.length() == 3) {
filteredMap.put(key, map.get(key));
}
}
return filteredMap;
}
}
For Java >= 8:
public static Map<String, String> filterMap(Map<String, String> map) {
return map.entrySet().stream()
.filter(entry -> entry.getKey().length() == 3)
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
}
For Java < 8:
public static Map<String, String> filterMap(Map<String, String> map) {
Map<String, String> res = new HashMap<>();
for (Map.Entry<String, String> entry : map.entrySet())
if (entry.getKey().length() == 3)
res.put(entry.getKey(), entry.getValue());
return res;
}

How to return new non abstract Map from lambda expression?

Could you guys help me how to get result as List<Map<Long, MapDifference> instead of List<AbstractMap.SimpleEntry<Long, MapDifference>>?
Input is List. Object has id, and two different List objects - left and right. I want to compare them and associate difference with id. Then return that whole list of MapDifferences with id
I have following piece of code:
List<AbstractMap.SimpleEntry<Long, MapDifference>> mapDifferences = input
.stream()
.map(h -> {
Optional<Map<String, List<String>>> left = Optional.ofNullable(..Object1.);
Optional<Map<String, List<String>>> right = Optional.ofNullable(..Object2..);
MapDifference mapDifference = Maps.difference(left.orElseGet(LinkedTreeMap::new), right.orElseGet(LinkedTreeMap::new));
return new AbstractMap.SimpleEntry<Long, MapDifference>((long) h.get("property"), mapDifference);
})
.collect(Collectors.toList());
First Optional::ofNullable should not be used to do a simple null check. Next you can use Collections::singletonMap and your code look like:
List<Map<Long, MapDifference>> mapDifferences = input
.stream()
.map(h -> {
Map<String, List<String>> left = object1 == null ? new LinkedTreeMap<>() : object1;
Map<String, List<String>> right = object2 == null ? new LinkedTreeMap<>() : object2;
MapDifference mapDifference = Maps.difference(left, right);
return Collections.singletonMap((long) h.get("property"), mapDifference);
})
.collect(Collectors.toList());
Or if you want to flatten your structure and there are only unique property numbers, then use:
Map<Long, MapDifference> mapDifferences = input
.stream()
.map(h -> {
Map<String, List<String>> left = object1 == null ? new LinkedTreeMap<>() : object1;
Map<String, List<String>> right = object2 == null ? new LinkedTreeMap<>() : object2;
MapDifference mapDifference = Maps.difference(left, right);
return new AbstractMap.SimpleEntry<>((long) h.get("property"), mapDifference);
})
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
As far as I understand you need Map as Silvio mentioned. Following code returns map which keys is input id and corresponding value is a difference between this input left and right list. I skipped calculating difference.
class Example {
static class Input {
Long id;
}
static class MapDifference {
}
public static void main(String[] args) {
Stream.of(new Input(), new Input())
.collect(Collectors.toMap(
input -> input.id,
input -> {
// calculate difference
return new MapDifference();
}
));
}
}

Categories