How to conditionally modify a Map in Java 8 stream API? - java

I am trying to modify a Map's keys based on conditional logic and struggling. I'm new to Java 8 streams API. Let's say I have a map like this:
Map<String, String> map = new HashMap<>();
map.put("PLACEHOLDER", "some_data1");
map.put("Google", "some_data2");
map.put("Facebook", "some_data3");
map.put("Microsoft", "some_data4");
When I would like to do is find the references of PLACEHOLDER and conditionally change that key to something else based on a boolean condition. I feel like it should be something like the below, but this doesn't even compile of course.
boolean condition = foo();
map = map.entrySet().stream().filter(entry -> "PLACEHOLDER".equals(entry.getKey()))
.map(key -> {
if (condition) {
return "Apple";
} else {
return "Netflix";
}
}).collect(Collectors.toMap(e -> e.getKey(), Map.Entry::getValue));
I found this question which kind of makes me think maybe I can't do this with Java 8 stream APIs. Hopefully someone better at this than me knows how to do this. Ideone link if you want to play with it.

You've filtered out all elements that aren't PLACEHOLDER. You need to add that filter logic to your map operation:
final Map<String, String> output = input.entrySet().stream()
.map(e -> {
if (!e.getKey().equals("PLACEHOLDER")) {
return e;
}
if (condition) {
return new AbstractMap.SimpleImmutableEntry<>("Apple", e.getValue());
}
return new AbstractMap.SimpleImmutableEntry<>("Netflix", e.getValue());
}).collect(toMap(Map.Entry::getKey, Map.Entry::getValue));
But as you are guaranteed to only have a single instance of PLACEHOLDER in the Map, you can just do
String placeholderData = input.remove("PLACEHOLDER");
if (placeholderData != null) {
input.put(condition ? "Apple" : "Netflix", placeholderData);
}

If you really want to do it using Streams, you just need to move the conditional logic to the collection phase, like that:
boolean condition = true;
map.entrySet().stream().collect(Collectors.toMap(
entry -> mapKey(entry.getKey(), condition), Map.Entry::getValue
));
where:
private static String mapKey(String key, boolean condition) {
if (!"PLACEHOLDER".equals(key)) {
return key;
}
if (condition) {
return "Apple";
} else {
return "Netflix";
}
}
However, the second part of Boris the Spider's answer using Map.remove and Map.put seems the best way to go.

Related

Java 8 Group by and append to a set

I have function which returns a Map<String, Set<String>>, Code before java 8:
Map<String, Set<String>> degreeMap = new HashMap<>();
for(Course course : courses){
Set<String> cList = degreeMap.get(course.getCourseLevel().toString());
if(Objects.nonNull(cList)){
cList.addAll(course.getMasterDegree()); //this is what i want to append to the old set
degreeMap.put(course.getCourseLevel().toString(), cList);
} else{
degreeMap.put(course.getCourseLevel().toString(), new HashSet<>(course.getMasterDegree()));
}
}
return degreeMap;
Which return a map of courselevel -> set of degrees.
For example, it read all the courses and return a map like:
{"undergraduate" : ["BTech", "BSc", "BE"],
"masters": ["MTech", "MBA"],
"Executive": ["PGDBM", "EECP"]}
Here is my Course class:
public class Course {
private List<String> masterDegree;
private CourseLevel courseLevel;
}
But I want to write this piece of code in Java 8 style. For that, I tried this:
Map<String, Set<String>> degreeMap = courses.stream().collect(
Collectors.groupingBy(c -> c.getCourseLevel().toString(),
Collectors.mapping(c -> c.getMasterDegree(), Collectors.toSet()))
);
which is not working and I am getting the following compile-time error on this:
no instance(s) of type variable(s) exist so that List conforms to String inference variable T has incompatible bounds: equality constraints: String lower bounds: List
Any suggestion, how to achieve this?
Not tested, but looks like, you're looking for something like:
return courses.stream()
.collect(Collectors.toMap(course -> course.getCourseLevel().toString(),
course -> new HashSet<>(course.getMasterDegree()),
(set1, set2) -> Stream.of(set1, set2)
.flatMap(Set::stream).collect(Collectors.toSet())));
You might be interested in the Collectors.toMap() method.
Here is an example that you may need to tweak as I did not test it.
Map<String, Set<String>> degreeMap = courses.stream()
.collect(
Collectors.toMap(
item -> item.getCourseLevel().toString(), //your key mapping
item -> item.getMasterDegree(), //your value mapping
(oldValue, newValue) -> { //your process of aggregation
if (null == oldValue) {
return newValue;
} else {
oldValue.addAll(newValue);
return oldValue;
}
},
LinkedHashMap::new //your result initialiser
)
);
Also, another tip: you do not need to get by key and check for null, you can use the .compute(), .computeIfAbsent(), .computeIfPresent() methods on the map
I don't think you need lamda expression, you should refactor your codes to make it clear instead.
// supposed to be initialied with non-empty values
// Map<String, Set<String>> degreeMap = ...
for(Course course : courses){
// something like below
String key = course.getCourseLevel().toString();
// degreeSet should not be null
Set<String> degreeSet = course.getMasterDegree();
// TODO: check nullable
degreeSet.putAll(degreeMap.get(key));
degreeMap.put(key, degreeSet);
}
return degreeMap;

Refactor two for's into java 8 streams

I'm facing a small problem to rewrite my two for's into java 8 streams.
// This is a method parameter
Map<String, Collection<String>> preSelectedValues;
List<PersonModel> parameters = parameterSearchService.getParameterNames();
for(Iterator<Map.Entry<String, Collection<String>>> it = preSelectedValues.entrySet().iterator(); it.hasNext();) {
Map.Entry<String, Collection<String>> entry = it.next();
for(int i = 0; i < parameters.size(); i++) {
if (entry.getKey().startsWith(parameters.get(i).getName())) {
it.remove();
}
}
}
I've tried following streams to have the same behaviour as before:
Map<String, Collection<String>> filteredParameters = preSelectedValues.keySet().stream()
.filter(x -> isParameterValid(x, parameters))
.collect(Collectors.toMap(k -> k, v -> preSelectedValues.get(v)));
isParameterValid method:
private boolean isParameterValid(String parameterId, List<PersonModel> preSelectedValues) {
return preSelectedValues.stream()
.anyMatch(v -> !v.getName().startsWith(parameterId));
}
Basically what I'm trying to do is filter the "preSelectedValues" map which starts with "parameterId". But somehow when I'm using streams either it filters everything or nothing.
Your isParameterValid method doesn't have the same logic as the original loops, since:
You switched the instance and argument in the call to startsWith.
Calling anyMatch with a !v.getName().startsWith(parameterId) only tells you whether at least one element of the List<PersonModel> doesn't start with parameterId. Your original condition for keeping the entry in the Map was that all the elements of List<PersonModel> don't start with parameterId (or actually, the other way around - parameterId doesn't start with any of the names of elements of List<PersonModel>).
Therefore I negated the method to return the condition for removing an entry from the Map:
private boolean isParameterInvalid(String parameterId, List<PersonModel> preSelectedValues) {
return preSelectedValues.stream()
.anyMatch(v -> parameterId.startsWith(v.getName()));
}
And then the stream pipeline can look like this:
Map<String, Collection<String>> filteredParameters = preSelectedValues.entrySet().stream()
.filter(x -> !isParameterInvalid(x.getKey(), parameters))
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
BTW, since your original loops mutate the original Map, you can achieve the same with removeIf.
If you are trying to modify the original map you can use removeIf:
preSelectedValues.keySet().removeIf(
key -> parameters.stream()
.map(PersonModel::getName)
.anyMatch(key::startsWith)
);

Java generalize approach to validate the null in the object parameters

I am trying to implement a logic where I have a POJO class which has 7 attributes.
I have added these POJO classes into the map depends upon the value of the attributes.
Below is the implementation
Map<String,List<PriceClass>> map = new HashMap();
for (PriceClass price : prices) {
if (price.getAttribute1() !=null) {
if (map.get("attribute1") !=null) {
map.get("attribute1").add(price);
} else {
map.set("attibute1",Collections.singletonList(price))
}
} else if(price.getAttribute2()!=null) {
if (map.get("attribute12") !=null) {
map.get("attribute2").add(price);
} else {
map.set("attibute2",Collections.singletonList(price))
}
} else if (price.getAttribute3() !=null) {
.
.
.
} else if (price.getAttribute7() !=null) {
//update the map
}
}
My question is rather than writing these many if loops are there any generalize implementations I can try here.
You may use
Map<String,List<PriceClass>> map = new HashMap<>();
for(PriceClass price: prices) {
HashMap<String,Object> options = new HashMap<>();
options.put("attibute1", price.getAttribute1());
options.put("attibute2", price.getAttribute2());
options.put("attibute3", price.getAttribute3());
options.put("attibute4", price.getAttribute4());
options.put("attibute5", price.getAttribute5());
options.put("attibute6", price.getAttribute6());
options.put("attibute7", price.getAttribute7());
options.values().removeIf(Objects::isNull);
options.keySet().forEach(attr -> map.computeIfAbsent(attr, x -> new ArrayList<>())
.add(price));
}
or generalizing the process:
Prepare a unmodifiable map once
static final Map<String, Function<PriceClass,Object>> ATTR;
static {
Map<String, Function<PriceClass,Object>> a = new HashMap<>();
a.put("attibute1", PriceClass::getAttribute1);
a.put("attibute2", PriceClass::getAttribute2);
a.put("attibute3", PriceClass::getAttribute3);
a.put("attibute4", PriceClass::getAttribute4);
a.put("attibute5", PriceClass::getAttribute5);
a.put("attibute6", PriceClass::getAttribute6);
a.put("attibute7", PriceClass::getAttribute7);
ATTR = Collections.unmodifiableMap(a);
}
and use either
Map<String,List<PriceClass>> map = new HashMap<>();
for(PriceClass price: prices) {
HashMap<String,Object> options = new HashMap<>();
ATTR.forEach((attr,func) -> options.put(attr, func.apply(price)));
options.values().removeIf(Objects::isNull);
options.keySet().forEach(attr -> map.computeIfAbsent(attr, x -> new ArrayList<>())
.add(price));
}
or
Map<String,List<PriceClass>> map = prices.stream()
.flatMap(price -> ATTR.entrySet().stream()
.filter(e -> e.getValue().apply(price) != null)
.map(e -> new AbstractMap.SimpleEntry<>(e.getKey(), price)))
.collect(Collectors.groupingBy(Map.Entry::getKey,
Collectors.mapping(Map.Entry::getValue, Collectors.toList())));
A likely optimal solution would be similar to one I have suggested earlier today.
Use the Map<String, Optional<?>> to store the Optional values of the checked attributes with a key of the future output map key.
Map<String, Optional<?>> options = new HashMap<>();
options.put("attribute1", Optional.ofNullable(price.getAttribute1()));
// ...
options.put("attribute3", Optional.ofNullable(price.getAttribute2()));
// ...
Using the iteration of the indices would let you perform the update of a map.
Map<String,List<Price>> map = new HashMap();
for (int i=1; i<7; i++) { // attributes 1..7
String attribute = "attribute" + i; // attribute1...attribute7
options.get(attribute).ifPresent(any -> // for non-nulls
map.put( // put to the map
attribute, // attribute as key remains
Optional.ofNullable(map.get(attribute)) // gets the existing list
.orElse(new ArrayList<>()) // or creates empty
.add(price))); // adds the current Price
}
Moreover, I bet your intention was a bit different. There is no method Map::set
map.set("attibute1",Collections.singletonList(price))
Didn't you mean to put a List<Price> with one item to the very same key instead?
map.put("attibute1", Collections.singletonList(price))
For this reason you can use the way I posted above.
What about using e.g. Enum to define 7 different objects each of them is responsible for concrete attribute:
// this is client code, looks pretty easy
Map<String, List<PriceClass>> map = new HashMap<>();
for (PriceClass price : prices)
PriceAttribute.add(map, price);
// all logic is hidden within special Enum
enum PriceAttribute {
ATTRIBUTE1("attribute1", PriceClass::getAttribute1),
ATTRIBUTE2("attribute2", PriceClass::getAttribute2),
ATTRIBUTE3("attribute3", PriceClass::getAttribute3),
ATTRIBUTE4("attribute4", PriceClass::getAttribute4),
ATTRIBUTE5("attribute5", PriceClass::getAttribute5),
ATTRIBUTE6("attribute6", PriceClass::getAttribute6),
ATTRIBUTE7("attribute7", PriceClass::getAttribute7);
private final String key;
private final Function<PriceClass, ?> get;
PriceAttribute(String key, Function<PriceClass, ?> get) {
this.key = key;
this.get = get;
}
public static void add(Map<String, List<PriceClass>> map, PriceClass price) {
for (PriceAttribute attribute : values()) {
if (attribute.get.apply(price) != null) {
map.computeIfAbsent(attribute.key, key -> new ArrayList<>()).add(price);
break;
}
}
}
}
Following is repetitive code:
if(map.get("attribute1") !=null)
{
map.get("attribute1").add(price);
}
else
{
map.set("attibute1",Collections.singletonList(price))
}
It could be refactored into a method, and called from the parent method which could make this look a bit cleaner.
Additionally you can also try
prices.removeAll(Collections.singleton(null)) and then run loop through it, to avoid one "If" condition.
Following code snippet can be added as a method.
if(map.get("attribute1") !=null) {
map.get("attribute1").add(price);
} else {
map.set("attibute1",Collections.singletonList(price))
}
to
private static void addPrice(String attributeName, Price price){
if(map.get(attributeName) !=null) {
map.get(attributeName).add(price);
} else {
map.set(attributeName,Collections.singletonList(price))
}
}
Also, map should be created static to be used in this case.
That wasn't be strict answer to the question but I want to improve your code.
You call get twice. Instead of this:
if(map.get("attribute1") !=null) {
map.get("attribute1").add(price);
} else {
map.set("attibute1",Collections.singletonList(price))
}
Use this:
final List<PriceClass> attribute1 = map.get("attribute1");
if (attribute1 != null) {
attribute1.add(price);
} else {
map.set("attibute1", Collections.singletonList(price))
}
Second you use Collections.singletonList which create immutable list so if you try to add something to it (and you do it) you'll get exception. You should use
new ArrayList<PriceClass>(Arrays.asList(price)) or
Stream.of(price).collect(toList())
If this is the real code you have, so create an array of attribute
Attribute[] attributesOf(Price price){
Attribute[] a = new Attribute[7];
a[0] = price.getAttribute1();
a[1] = price.getAttribute2();
...
a[6] = price.getAttribute7();
}
when you have more attributes, just modify this method. Then your code can be refactor to
for(PriceClass price : prices){
Attribute[] attributes = attributesOf(price);
for(int i=0;i<attributes.length;i++){
String key = "attribute" + (i+1);
if(attributes[i] != null){
map.get(key).add(price);
}
else{
map.set(key, Collections.singletonList(price));
}
}
}
But if you code are different, like price.getGrossAmount(), price.getNetAmount(), price.getTax() you have to find the general contract to define the type of array.
Nevertheless, you have to understand that good data structure will make your code simple and perform well. Maybe you don't need to define attribute in the price class. Maybe you don't need the price class, but you can use BigDecimal as the price value instead.
I don't know your context well, I don't know what are you doing. I think you might have a better answer from someone else if you show the real code as well as its context.
Acc. to your question, it seems like if attibute1 is present in a PriceClass object, it will go to the attibute1 key. This means if all objects in prices list has attibute1, the whole list will go the attibute1 key.
With the above assumption, here is a java-8 solution containing streams.
public static void main(String[] args){
Map<String,List<PriceClass>> map = new HashMap<>();
List<PriceClass> prices =new ArrayList<>();
prices.add(new PriceClass(1,2,3,4,5,6,7));
prices.add(new PriceClass(null,12,13,14,15,16,17));
map = prices.stream()
.map(priceClass -> Arrays
.stream(PriceClass.class.getDeclaredFields())
.map(field -> getFieldValue(field, priceClass))
.filter(Objects::nonNull)
.findFirst()
.orElse(null)
)
.filter(Objects::nonNull)
.collect(Collectors.groupingBy(AbstractMap.SimpleEntry::getKey,
Collectors.mapping(AbstractMap.SimpleEntry::getValue,
Collectors.toList())));
System.out.println(map);
}
private static AbstractMap.SimpleEntry<String,PriceClass> getFieldValue(Field field, PriceClass priceClass){
Optional<Integer> value = Optional.empty();
try {
value = Optional.ofNullable((Integer)field.get(priceClass));
} catch (IllegalAccessException e) {
e.printStackTrace();
}
if (value.isPresent()) {
return new AbstractMap.SimpleEntry<>(field.getName(), priceClass);
}
return null;
}
Output:
{attribute1=[PriceClass{attribute1=1, attribute2=2, attribute3=3, attribute4=4, attribute5=5, attribute6=6, attribute7=7}],
attribute2=[PriceClass{attribute1=null, attribute2=12, attribute3=13, attribute4=14, attribute5=15, attribute6=16, attribute7=17}]}

how to write a foreach with more if/else conditions in java8?

I'am trying to develop a method in java 8 with some foreach and more than one if condition.
I don't khnow if i can write for any if condition a filter? and how to do that in java 8?
Here is my methode:
public Map<LocalDate,boolean[]> getForkedDetails() {
TreeMap<LocalDate,boolean[]> map=new TreeMap<>();
this.getDetails().forEach(dept->{
boolean[] myArray=new boolean[3];
if(dept.getTypemt().equals(TypeMt.SIP)) {
myArray[0]=Boolean.TRUE;
}
if(dept.getTypemt().equals(TypeMt.DOP)) {
myArray[1]=Boolean.TRUE;
}
if(dept.getTypemt().equals(TypeMt.OPA) ) {
myArray[2]=Boolean.TRUE;
}
if(map.containsKey(dept.getDateFa())){
boolean[] bs = map.get(dept.getDateFa());
for(int index=0;index<bs.length;index++){
if(myArray[index]){
bs[index]=myArray[index];
}
}
map.put(dept.getDateFa(), bs);
}else{
map.put(dept.getDateFa(), myArray);
}
});
// get all dates between start and end dates
List<LocalDate> dates = Stream.iterate(this.getDateDebut(), date -> date.plusDays(1))
.limit(ChronoUnit.DAYS.between(this.getDateDebut(), this.getDateFin()))
.collect(Collectors.toList());
dates.forEach(date->{
if(!map.containsKey(date)){
map.put(date, new boolean[3]);
}
});
// Sorted TreeMap
TreeMap<LocalDate,boolean[]> result = map.entrySet().stream()
.sorted(Map.Entry.comparingByKey())
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue,
(oldValue, newValue) -> oldValue, TreeMap::new));
return result;
}
Before redesigning this further, there are a lot of things to clean up. First, the baroque array creation, then, using containsKey followed by get or put bears several unnecessary map lookups. You can use merge instead. Then, there is no need to collect a stream into a List, just to apply forEach on it. You can use forEach on the stream in the first place. Well, and TreeMaps are always sorted by key, there is no sense in performing a stream operation to sort it, just to collect into a TreeMap that will sort the entries by itself.
public Map<LocalDate,boolean[]> getForkedDetails() {
TreeMap<LocalDate,boolean[]> map=new TreeMap<>();
this.getDetails().forEach(dept -> {
boolean[] myArray= { dept.getTypemt().equals(TypeMt.SIP),
dept.getTypemt().equals(TypeMt.DOP),
dept.getTypemt().equals(TypeMt.OPA) };
map.merge(dept.getDateFa(), myArray, (bs,newArray) -> {
for(int index=0;index<bs.length;index++){
if(newArray[index]) bs[index]=true;
}
return bs;
});
});
// add entries for all missing dates between start and end dates
Stream.iterate(this.getDateDebut(), date -> date.plusDays(1))
.limit(ChronoUnit.DAYS.between(this.getDateDebut(), this.getDateFin()))
.forEach(date-> map.computeIfAbsent(date, key -> new boolean[3]));
// the TreeMap is already sorted
return map;
}
Then, the first part can be rewritten as
TreeMap<LocalDate,boolean[]> map = this.getDetails()
.stream()
.collect(Collectors.toMap(
dept -> dept.getDateFa(),
dept -> new boolean[] { dept.getTypemt().equals(TypeMt.SIP),
dept.getTypemt().equals(TypeMt.DOP),
dept.getTypemt().equals(TypeMt.OPA) },
(bs,newArray) -> {
for(int index=0;index<bs.length;index++){
if(newArray[index]) bs[index]=true;
}
return bs;
},
TreeMap::new));
Going off Lino's comment should be a way to go. Instead of all the spam with if-elseif-else just do what your condition is. It returns a boolean I assume so you should be able to just do that.
Your answer basically is don't use them because you don't need them. Short and sweet code is the best code (unless you then can't understand it code golfers)

How to perform filtering by Key on KeyValue objects using Lambda-Expressions?

Given i want to filter a List of Key-Value objects.
My (Document)-Object from the example below looks like this
{
"attributeEntityList" : [
{key: 'key1', value: 'somevalue1'},
{key: 'key2', value: 'somevalue2'},
{key: 'key3', value: 'somevalue3'}
]
}
When I pass in a list of the following keys ["key1", "key2", "key3"], I expect my function to return the whole given List of attributes.
When I pass in a list of the following keys ["key1", "key2"], I expect my function to return a list of Attributes with the given key-names.
When I pass in a list of the following keys ["key1", "key2", "faultyKey"], I expect my function to return an Empty list.
My imperative-style solution looks like this and it works okay:
private List<AttributeEntity> getAttributeEntities(List<String> keys, Document value) {
final List<AttributeEntity> documentAttributeList = value.getAttributeEntityList();
final List<AttributeEntity> resultList = new ArrayList<>();
for(String configKey: keys){
boolean keyInAttribute = false;
for(AttributeEntity documentAttribute : documentAttributeList){
if(configKey.equals(documentAttribute.getAttribute_key())){
keyInAttribute = true;
resultList.add(documentAttribute);
break;
}
}
if(!keyInAttribute){
resultList.clear();
break;
}
}
return resultList;
}
For education and fun (and maybe better scaling) I'd like to know how to convert this piece of Code into a solution using the new Java 8 streaming-api.
This is what I came up with, converting my pre-Java8-code to Java8.
To my eyes it looks much more concise and it's shorter. But it does not, what I expect it to do :/
I'm realy struggling implementing the third bulletpoint of my requirements.
It always returns all (found) Attributes, even when i pass in a not existant key.
private List<AttributeEntity> getAttributeEntities(List<String> keys, Document value) {
final List<AttributeEntity> documentAttributeList = value.getAttributeList();
return documentAttributeList.stream()
.filter(attribute ->
keys.contains(attribute.getAttribute_key())
).collect(Collectors.toList());
}
I'm thinking of implementing my own custom Collector.
Since my Collector should only return the List, when the collected results contain each given key at least once.
Any other Idea on how to achieve that?
This solution passes my tests.
But it feel's like i'm putting the cart before the horse.
It's neither concise nor short or elegant any more.
private List<AttributeEntity> getAttributeEntities(List<String> keys, Document value) {
final List<AttributeEntity> documentAttributeList = value.getAttributeList();
return documentAttributeList.stream()
.filter(attribute ->
keys.contains(attribute.getAttribute_key())
)
.collect(Collectors.collectingAndThen(Collectors.toList(), new Function<List<AttributeEntity>, List<AttributeEntity>>() {
#Override
public List<AttributeEntity> apply(List<AttributeEntity> o) {
System.out.println("in finisher code");
if (keys.stream().allMatch(key -> {
return o.stream().filter(attrbiute -> attrbiute.getAttribute_key().equals(key)).findAny().isPresent();
})) {
return o;
} else {
return new ArrayList<AttributeEntity>();
}
}
}));
}
First of all I must say that I'm also new at Java 8 features, so I'm not familiar with everything, and not very used to functional programming. I tried a different approach, dividing it all into some methods.
Here it is:
public class Main {
private static List<AttributeEntity> documentAttributeList;
static {
documentAttributeList = new ArrayList<>();
documentAttributeList.add(new AttributeEntity("key1", "value1"));
documentAttributeList.add(new AttributeEntity("key2", "value2"));
documentAttributeList.add(new AttributeEntity("key3", "value3"));
}
public static void main(String[] args) {
Main main = new Main();
List<AttributeEntity> attributeEntities = main.getAttributeEntities(Arrays.asList("key1", "key2"));
for (AttributeEntity attributeEntity : attributeEntities) {
System.out.println(attributeEntity.getKey());
}
}
private List<AttributeEntity> getAttributeEntities(List<String> keys) {
if(hasInvalidKey(keys)){
return new ArrayList<>();
} else {
return documentAttributeList.stream().filter(attribute -> keys.contains(attribute.getKey())).collect(toList());
}
}
private boolean hasInvalidKey(List<String> keys) {
List<String> attributeKeys = getAttributeKeys();
return keys.stream().anyMatch(key -> !attributeKeys.contains(key));
}
private List<String> getAttributeKeys() {
return documentAttributeList.stream().map(attribute -> attribute.getKey()).collect(toList());
}
}
If a document can never have multiple attributes with the same name, I think you can do it like this (don't have a compiler handy to try):
Map<String, AttributeEntity> filteredMap=value.getAttributeEntityList().stream()
.filter(at->keys.contains(at.getKey()))
.collect(toMap(at->at.getKey(), at->at));
return filteredMap.keySet().containsAll(keys)
? new ArrayList<>(filteredMap.values())
: new ArrayList<>();
If multiple attributes per name are allowed, you would have to use groupingBy instead of toMap. You can, of course, rewrite this with collectingAndThen but I think it would be less clear.
I came up with something.
I don't know if it it the most elegant solution but at least it works and i can reason about it.
private List<AttributeEntity> getAttributeEntities(List<String> keys, Document value) {
final List<AttributeEntity> documentAttributeList = value.getAttributeList();
boolean allKeysPresentInAnyAttribute = keys.stream()
.allMatch(key ->
documentAttributeList.stream()
.filter(attrbiute ->
attrbiute.getAttribute_key().equals(key)
)
.findAny()
.isPresent()
);
if (allKeysPresentInAnyAttribute) {
return documentAttributeList.stream()
.filter(attribute ->
keys.contains(attribute.getAttribute_key())
)
.collect(Collectors.toList());
}
return new ArrayList<>();
}
Any hints or comments greatly appreciated.

Categories