Add Object to List after orElse - java

inscTipoTrabAval.getInscricaoTipoTrabalhoAvaliadorQuestoes()
.stream()
.filter(aq -> aq.getEventoQuestao().equals(eventoQuestao))
.findFirst()
.orElse(new InscricaoTipoTrabalhoAvaliadorQuestao(eventoQuestao, inscTipoTrabAval))
.setJustificativa(justificativa);
I'm trying to write an object into a list if it doesn't exist with orElse, but it isn't adding to the list. Is there any way to do this?

The easiest is to store the list in a variable, and check the content of your optional
//assuming the list is not immutable
List<InscricaoTipoTrabalhoAvaliadorQuestao> list = inscTipoTrabAval.getInscricaoTipoTrabalhoAvaliadorQuestoes();
list.stream()
.filter(aq -> aq.getEventoQuestao().equals(eventoQuestao))
.findFirst()
.ifPresentOrElse(
existing -> existing.setJustificativa(justificativa),
() -> {
var value = new InscricaoTipoTrabalhoAvaliadorQuestao(eventoQuestao, inscTipoTrabAval));
value.setJustificativa(justificativa);
list.add(value);
}
);
If you're on Java 8, you can use an if block
Optional<InscricaoTipoTrabalhoAvaliadorQuestao> value = list.stream()
.filter(aq -> aq.getEventoQuestao().equals(eventoQuestao))
.findFirst()
if(value.isPresent()) {
value.get().setJustificativa(justificativa);
} else {
InscricaoTipoTrabalhoAvaliadorQuestao newValue = new InscricaoTipoTrabalhoAvaliadorQuestao(eventoQuestao, inscTipoTrabAval));
newValue.setJustificativa(justificativa);
list.add(newValue);
}

Related

How to exclude non-single items from two lists?

How can I use a stream from two lists to get a list of unique entities?
Match only by username
public class Entity {
private String username;
private String password;
}
var first = Arrays.asList(
new Entity("user1", ""),
new Entity("user2", "")
new Entity("user3", "pass3"),
new Entity("user5", "pass5")
);
var second = Arrays.asList(
new Entity("user1", "pass1"),
new Entity("user2", "pass2"),
);
public static void foo(List<Entity> first, List<Entity> second) {
List<Entity>result = Stream.of(first, second)
.flatMap(List::stream)
?
?
.collect(Collectors.toList());
}
result must be list with Entity("user3", "pass3") and Entity("user5", "pass5")
you can make grouping by username:
var groupedData = Stream.concat(list1.stream(), list2.stream())
.collect(Collectors.groupingBy(Entity::getUsername));
and then filtered entity which size > 1:
groupedData.values().stream()
.filter(s -> s.size() == 1)
.flatMap(Collection::stream)
.collect(Collectors.toList());
or only one a big stream:
Stream.concat(list1.stream(), list2.stream())
.collect(Collectors.groupingBy(Entity::getUsername)).values().stream()
.filter(s -> s.size() == 1)
.flatMap(Collection::stream)
.collect(Collectors.toList());
Along with using groupingBy you can also use Collectors.toMap with merging (val1, val2) -> null to exclude elements getting to merge thus leaving only single elements:
List<Entity> result = Stream.concat(first.stream(), second.stream())
.collect(Collectors.toMap(Entity::getUsername,
val -> val, (val1, val2) -> null))
.values().stream()
.collect(Collectors.toList());
Probably it's not the best way
public static List<Entity> foo(List<Entity> first, List<Entity> second) {
List<Entity> arr = new ArrayList<>();
arr.addAll(first);
arr.addAll(second);
return arr
.stream()
.filter(entity -> (first.stream().map(Entity::getUsername).anyMatch(username -> username.equals(entity.getUsername())) &&
second.stream().map(Entity::getUsername).noneMatch(username -> username.equals(entity.getUsername()))) ||
(second.stream().map(Entity::getUsername).anyMatch(username -> username.equals(entity.getUsername())) &&
first.stream().map(Entity::getUsername).noneMatch(username -> username.equals(entity.getUsername()))))
.collect(Collectors.toList());
}
The logic in the filter is "Exclusive OR", as we don't have a straight way of doing that, we need to make the logic of (Condition1 and not Condition2) or (Condition2 and not Condition1).
lambda as a straight forward solution
concat the streams of first-list-entities not contained in second-list and vice versa
List<Entity> unique = Stream.concat(
first.stream().filter(e -> ! second.contains( e )),
second.stream().filter(e -> ! first.contains( e )) ).collect( toList() );

How to filter based on list returned by map param using Java 8 streams

I'm trying to use Java stream to filter some values based on certain conditions. I am able to achieve the same using traditional for loops and a little bit of streams, but I want to rewrite the same logic fully in streams.
Original code:
public List <String> getProductNames(Hub hub, String requestedGroup) {
List <SupportedProduct> configuredProducts = repo.getSupportedProducts(hub);
List <String> productNames = new ArrayList <> ();
for (SupportedProduct supportedProduct: configuredProducts) {
List < String > categoryNameList = new ArrayList <> ();
String activeCategoryName = supportedProduct.getCategoryDetails().getActiveCategoryName();
if (activeCategoryName == null) {
Optional.ofNullable(supportedProduct.getCategoryDetails().getCategories())
.orElse(Collections.emptyList())
.forEach(category - > categoryNameList.add(category.getName()));
} else {
categoryNameList.add(activeCategoryName);
}
for (String catName: categoryNameList) {
Division division = divisionRepo.getDivisionByCatName(catName);
if (division != null && division.getGroup() == requestedGroup) {
productNames.add(supportedProduct.getProductName());
}
}
}
return productNames;
}
My try:
return Optional.ofNullable(configuredProducts).orElse(Collections.emptyList()).stream()
.map(supportedProduct -> {
List<String> categoryNameList = new ArrayList<>();
String activeCategoryName = supportedProduct.getCategoryDetails().getActiveCategoryName();
if (activeCategoryName == null) {
Optional.ofNullable(supportedProduct.getCategoryDetails().getCategories())
.orElse(Collections.emptyList())
.forEach(category -> categoryNameList.add(category.getName()));
} else {
categoryNameList.add(activeCategoryName);
}
return categoryNameList;
})
.filter(catName ->{
Division division = divisionRepo.getDivisionByCatName(catName);
return division != null && division.getGroup() == requestedGroup;
})........
But I'm lost beyond this.
Please help me to write the same using streams.
EDIT: Added IDEOne for testing - Link
The logic inside is quite complicated, however, try this out:
public List <String> getProductNames(Hub hub, String requestedGroup) {
List<SupportedProduct> configuredProducts = repo.getSupportedProducts(hub);
// extract pairs:
// key=SupportedProduct::getProductName
// values=List with one activeCategoryName OR names of all the categories
Map<String, List<String>> namedActiveCategoryNamesMap = configuredProducts.stream()
.collect(Collectors.toMap(
SupportedProduct::getProductName,
p -> Optional.ofNullable(p.getCategoryDetails().getActiveCategoryName())
.map(Collections::singletonList)
.orElse(Optional.ofNullable(p.getCategoryDetails().getCategories())
.stream()
.flatMap(Collection::stream)
.map(Category::getName)
.collect(Collectors.toList()))));
// look-up based on the categories' names, group equality comparison and returning a List
return namedActiveCategoryNamesMap.entrySet().stream()
.filter(entry -> entry.getValue().stream()
.map(catName -> divisionRepo.getDivisionByCatName(catName))
.filter(Objects::nonNull)
.map(Division::getGroup)
.anyMatch(requestedGroup::equals))
.map(Map.Entry::getKey)
.collect(Collectors.toList());
}
I recommend splitting into separate methods for sake of readability (the best way to go).
The verbose logics of Optional chains including two orElse calls can be surely simplified, however, it gives you the idea.
You can perform within one Stream using Collectors.collectingAndThen. In that case, I'd extract the Function finisher elsewhere, example:
public List<String> getProductNames(Hub hub, String requestedGroup) {
return repo.getSupportedProducts(hub).stream()
.collect(Collectors.collectingAndThen(
Collectors.toMap(
SupportedProduct::getProductName,
categoryNamesFunction()),
productNamesFunction(requestedGroup)));
}
private Function<Map<String, List<String>>, List<String>> productNamesFunction(String requestedGroup) {
return map -> map.entrySet().stream()
.filter(entry -> entry.getValue().stream()
.map(divisionRepo::getDivisionByCatName)
.filter(Objects::nonNull)
.map(Division::getGroup)
.anyMatch(requestedGroup::equals))
.map(Map.Entry::getKey)
.collect(Collectors.toList());
}
private Function<SupportedProduct, List<String>> categoryNamesFunction() {
return p -> Optional.ofNullable(p.getCategoryDetails().getActiveCategoryName())
.map(Collections::singletonList)
.orElse(Optional.ofNullable(p.getCategoryDetails().getCategories())
.stream()
.flatMap(Collection::stream)
.map(Category::getName)
.collect(Collectors.toList()));
}

How do you call a method from within a Collector in Java 8 Streaming?

I have a bit of code that is working functionally as I want it to:
private Map<Florist, EnumSet<Flower>> localMethod(List<FlowerSellers> branchList) {
Map<Florist, EnumSet<Flower>> availableFlowers = new EnumMap<>(Florist.class);
branchList.stream()
.filter(f -> f instanceof FlorestFranchised)
.forEach(f -> availableFlowers.put(
FlorestHelperClass.florestTypeForKey(f.id()),
((FlorestFranchised) f).getFlowers()));
return availableFlowers;
}
For sake of argument the helper class method is:
public static Florist FlorestTypeForKey(String id) {
return FLOREST_MAP.get(id);
}
I would like to do this with a collector instead so I wanted to do this ...
return branchList.stream()
.filter(p -> p instanceof FlorestFranchised)
.map(p -> (FlorestFranchised) p)
.collect(Collectors.toMap(
FlorestFranchised::getId,
FlorestFranchised::getFlowers));
But obviously this fails because it is not doing the helper class lookup and so rather than returning a map of <Florest, Flowers> it is returning <String, Flowers>
This fails though :
return branchList.stream()
.filter(p -> p instanceof FlorestFranchised)
.map(p -> (FlorestFranchised) p)
.collect(Collectors.toMap(
FlorestHelperClass.florestTypeForKey(FlorestFranchised::getId),
FlorestFranchised::getFlowers));
Can anyone tell me how I should call the lookup methods (FlorestHelperClass.florestTypeForKey) in the collector? Is it possible?
Collectors.toMap requires two functions creating a key and value respectively from the value passed into the collect method. Therefore you can do this:
return branchList.stream()
.filter(p -> p instanceof FlorestFranchised)
.map(p -> (FlorestFranchised) p)
.collect(Collectors.toMap(
p -> FlorestHelperClass.florestTypeForKey(p.getId()), // key
p -> p.getFlowers() // value
));
The last lambda expression can be replaced with FlorestFranchised::getFlowers.
You can map first as AbstractMap.SimpleEntry pair then use Collectors.toMap
return branchList
.stream()
.filter(p -> p instanceof FlorestFranchised )
.map(p -> (FlorestFranchised) p )
.map(p -> new AbstractMap.SimpleEntry<>(
FlorestHelperClass.florestTypeForKey(p.getId()), p.getFlowers()))
.collect(Collectors.toMap(Entry::getKey, Entry::getValue));

How to handle nullable lists using java 8?

I'm making a service call and trying to handle response.
Response might have a list of something. That list might be null.
Moreover, if list not null or not empty, then
it needs to be filtered.
In the code "entry" reference might be null if filtering gives nothing or response list is empty or null.
Currently i'm getting NPE when i try to use stream() on a null response list.
How can i handle this situation?
#Getter
public class ServiceResponse {
List<ResponseEntry> entryList;
}
#Getter
public class ResponseEntry {
String value;
}
ServiceResponse serviceResponse = service.getServiceResponse();
ResponseEntry entry = serviceResponse.getEntryList()
.stream()
.filter(e -> "expectedValue".equals(e.getValue()))
.findFirst()
.orElse(null);
if (entry == null) { ... }
if list not null or not empty, then it needs to be filtered.
No need for Optional here, as it's not intended to replace simple if checks.
ResponseEntry entry = null;
List<ResponseEntry> responseEntries = serviceResponse.getEntryList();
if(responseEntries != null && !responseEntries.isEmpty()){
entry = responseEntries.stream()
.filter(e -> "expectedValue".equals(e.getValue()))
.findFirst()
.orElse(null);
}
reads "if responseEntries is not null and responseEntries is not empty then apply the filter operation and find the first item or else null". Very readable.
On the other hand, the optional approach:
ResponseEntry entry = Optional.ofNullable(serviceResponse.getEntryList())
.orElseGet(() -> Collections.emptyList())
.stream()
.filter(e -> "expectedValue".equals(e.getValue()))
.findFirst();
if(!entry.isPresent()){ ... } // or entry.ifPresent(e -> ...) depending on the logic you're performing inside the block
unnecessarily creates objects that could be avoided and not really the intention of optional to be used as a substitute for simple "if" checks.
Stream.ofNullable (Java-9)
Returns a sequential Stream containing a single element, if non-null,
otherwise returns an empty Stream.
Current Code
ResponseEntry entry = serviceResponse.getEntryList() // List<ResponseEntry>
.stream() // NPE here // Stream<ResponseEntry>
.filter(e -> "expectedValue".equals(e.getValue())) // filter
.findFirst() // Optional<ResponseEntry>
.orElse(null); // or else null
Updated Code
ResponseEntry entry = Stream.ofNullable(serviceResponse.getEntryList()) // Stream<List<ResponseEntry>>
.flatMap(List::stream) // Stream<ResponseEntry>
.filter(e -> "expectedValue".equals(e.getValue())) // filter here
.findFirst() // Optional<ResponseEntry>
.orElse(null); // or else null
Optional.stream (Java-9)
returns a sequential Stream containing only that value, otherwise
returns an empty Stream.
ResponseEntry entry = Optional.ofNullable(serviceResponse.getEntryList())
.stream() // Stream<List<ResponseEntry>>
.flatMap(List::stream) // Stream<ResponseEntry>
.filter(e -> "expectedValue".equals(e.getValue())) // filter here
.findFirst() // Optional<ResponseEntry>
.orElse(null); // or else null
Optional.isEmpty(Java-11)
If a value is not present, returns true, otherwise false
Optional<ResponseEntry> entry = Optional.ofNullable(serviceResponse.getEntryList()) // Optional<List<ResponseEntry>>
.orElseGet(Collections::emptyList) // or else empty List
.stream() // Stream<ResponseEntry>
.filter(e -> "expectedValue".equals(e.getValue())) // filter
.findFirst(); // Optional<ResponseEntry>
if (entry.isEmpty()) { // !entry.isPresent in java-8
// Do your work here
}
In Java 9, you could use the new method Objects.requireNonNullElse(T,T):
Objects.requireNonNullElse(serviceResponse.getEntryList(),
Collections.emptyList())
Apache Commons Collections actually has a method ListUtils.emptyIfNull(List<T>) which returns an empty list if the argument list is null. That's even better, but Objects.requireNonNullElse is the closest thing to it in Java SE.
If you're restricted to just Java 8, then I agree with Aomine's answer that trying to do something like go through Optional is worse than an if statement.
You could simply use the ternary operator:
ServiceResponse serviceResponse = service.getServiceResponse();
List<ResponseEntry> list = serviceResponse.getEntryList();
ResponseEntry entry = (list == null ? Collections.emptyList() : list)
.stream()
.filter(e -> "expectedValue".equals(e.getValue()))
.findFirst()
.orElse(null);
if (entry == null) { ... }
Sometimes, traditional is better IMO.
Another option would be to use the Optional monad:
Optional<ResponseEntry> entry = Optional.ofNullable(serviceResponse.getEntryList()).flatMap(list ->
list.stream().filter(e -> "expectedValue".equals(e.getValue())).findFirst()
);
if (!entry.isPresent()) {
…
}
You might even use orElseGet instead of that if statement if your objective is to build (and return) a value, instead of executing a side effect.
I am new to Optional and I may be wrong. Logic can be written like below if you want to have logic including only optional.
ServiceResponse serviceResponse = service.getServiceResponse();
ResponseEntry entry =
Optional.of(CollectionUtils.isNotEmpty(serviceResponse.getEntryList()))
.filter(BooleanUtils::isTrue)
.stream()
.filter(e -> "expectedValue".equals(e.getValue()))
.findFirst()
.orElse(null);

Java stream create object and compare

I'm pretty new to Java streams. I've to split a string returned by filter in stream, create a new object with the strings in the split and compare each object with a predefined object. Stream looks like this (I know this is incorrect, just a representation of what I am trying to do):
xmlstream.stream()
.filter(xml->xml.getName()) //returns a string
.map(returnedString -> split("__"))
.map(eachStringInList -> new TestObj(returnedStr[0], returnedStr[1]))
.map(eachTestObj -> eachTestObj.compareTo(givenObj))
.max(Comparing.compare(returnedObj :: aProperty))
How do I achieve this? Basically map each string in list to create an object, compare that to a fix object and return max based on one of the properties.
Thanks.
You could use reduce like so:
TestObj predefined = ...
TestObj max =
xmlstream.stream()
.map(xml -> xml.getName()) //returns a string
.map(s -> s.split("__"))
.map(a -> new TestObj(a[0], a[1]))
.reduce(predifined, (e, a) ->
e.aProperty().compareTo(a.aProperty()) >= 0 ? e : a);
A more efficient version of the above would be:
TestObj predefined = ...
TestObj max =
xmlstream.stream()
.map(xml -> xml.getName()) //returns a string
.map(s -> s.split("__"))
.map(a -> new TestObj(a[0], a[1]))
.filter(e -> e.aProperty().compareTo(predefined.aProperty()) > 0)
.findFirst()
.orElse(predefined);
Update:
if you want to retrieve the max object by a given property from all the TestObj objects less than the predefined TestObj, then you can proceed as follows:
TestObj predefined = ...
Optional<TestObj> max =
xmlstream.stream()
.map(xml -> xml.getName())
.map(s -> s.split("_"))
.map(a -> new TestObj(a[0], a[1]))
.filter(e -> e.aProperty().compareTo(predefined.aProperty()) < 0)
.max(Comparator.comparing(TestObj::aProperty));
max returns an Optional<T>; if you're unfamiliar with it then consult the documentation here to familiarise you're with the different ways to unwrap an Optional<T> object.

Categories

Resources