Currently, that's my code:
Iterable<Practitioner> referencedPractitioners = this.practitionerRepository.findAllById(
Optional.ofNullable(patient.getPractitioners())
.map(List::stream)
.orElse(Stream.of())
.map(Reference::getIdPart)
.collect(Collectors.toList())
);
As you can see, I'm using this.practitionerRepository.findAllById(Iterable<String> ids), in order to get all using a single communication with database.
I was trying to change it using this:
Optional.ofNullable(patient)
.map(org.hl7.fhir.r4.model.Patient::getPractitioners)
.map(List::stream)
.orElse(Stream.of())
.map(Reference::getIdPart)
.collect(????????);
How could I use this.practitionerRepository.findAllById(Iterable<String> ids) into a custom collector in collect method?
Remember I need to get all entities at once. I can't get them one by one.
You can use Collectors.collectingAndThen(Collector<T,A,R> downstream, Function<R,RR> finisher) specialized collector for that.
Make a list of IDs using the Collector.toList() collector and then
Pass a reference practitionerRepository::findAllById to convert from List<String> to Iterable<Practitioner>
Example:
Iterable<Practitioner> referencedPractitioners = Optional.ofNullable(patient)
.map(Patient::getPractitioners)
.map(List::stream)
.orElseGet(Stream::of)
.map(Reference::getIdPart)
.collect(Collectors.collectingAndThen(toList(), practitionerRepository::findAllById));
Related
This question already has answers here:
How to Convert List<String> to List<Object>
(7 answers)
Closed last year.
I have a Collection of Strings which I want to iterate and do a DB query on each of them and collect the response of each query into a Collection of Objects. I am sure we can do this through a for loop iterator but is there a way to do it with Java8 Streams ? This is what I came up with -
static Collection<Action> getActions(Collection<String> actionIds, RequestContext rc) {
List<Collection<Action>> ac = actionIds.stream().map(str -> hashmap.get(str)).collect(Collectors.toList());
return ac.get(0);
}
Action is a custom class. I read that I may need to do something like this - https://itsallbinary.com/java-8-create-custom-streams-collector/ .
Is this necessary ? Or any easier ways ?
If I use this .collect(toCollection(ArrayList::new)), it gives me Collection<Collection<Action>>
It seems that hashmap is a Map<String, Collection<Action>> which can be converted into filtered Collection<Action> using Stream::flatMap, as suggested in the comments.
Possibly it's worth to filter available actionIds as a defensive measure. Also, some specific implementation of the collection needs to be selected or at least Set or List depending on the actual requirements (e.g. if the order of the actions needs to be maintained, or if the duplicate actions need to be filtered, etc.)
Most likely, a mere Collectors::toList would be fine for the result:
static Collection<Action> getActions(Collection<String> actionIds, RequestContext rc) {
return actionIds.stream()
.filter(hashmap::containsKey) // make sure nulls not returned
.map(hashmap::get) // Stream<Collection<Action>>
.flatMap(Collection::stream) // Stream<Action>
.collect(Collectors.toList()); // List<Action>
}
"Unique" values may be retrieved using Stream::distinct before collecting to list or by collecting to a set instead of list (using LinkedHashSet to maintain the order of insertion):
static Collection<Action> getDistinctActions(Collection<String> actionIds, RequestContext rc) {
return actionIds.stream()
.filter(hashmap::containsKey) // make sure nulls not returned
.flatMap(actions -> actions.stream()) // Stream<Action>
.collect(Collectors.toCollection(LinkedHashSet::new));
}
I get an PublicException: Duplicate Keys error in this place.
Map<BgwContract, List<Fee>> bgwContractFeeMap = bgwContractList
.stream()
.filter(bgwContract -> !bgwContract.getStatus().equals(BgwContractStatus.CLOSED))
.filter(bgwContract -> availableIbans.contains(bgwContract.getFeeAccount()))
.collect(
Collectors.toMap(bgwContract -> bgwContract,
bgwContractFeeService::getContractMonthlyFees)
);
I understand that the issue is that there are some duplicates and it immediately crashes. I know that a .distinct() would fix this error, but I don't want to lose any data. Is there a way how to enhance this mapping to fix this error without loosing any values, maybe some kind of a filter or any other kind of java 8 methods? I'm not talking about MultiMaps etc.
You need to pass a merge function to Collectors.toMap(), which handles values having the same key:
Map<BgwContract, List<Fee>> bgwContractFeeMap = bgwContractList
.stream()
.filter(bgwContract -> !bgwContract.getStatus().equals(BgwContractStatus.CLOSED))
.filter(bgwContract -> availableIbans.contains(bgwContract.getFeeAccount()))
.collect(
Collectors.toMap(Function.identity(),
bgwContractFeeService::getContractMonthlyFees,
(l1,l2)->{
l1.addAll(l2);
return l1;
})
);
In this case, the elements of two value lists having the same key will be concatenated into a single list.
I have the following POJO:
public class Order {
private String name;
private String status;
private BigDecimal total;
// getters, setters and ctors down here
}
I am looping through a List<Order> and trying to update all their status fields to a value of "ORDERED". The old (pre Streaming API) way of doing this was:
for (Order order : orders) {
order.setStatus("ORDERED");
}
I'm trying to figure out the Java 8 ("Streaming") way of accomplishing the same thing. My best attempt thus far:
orders.stream().map(order -> order.setStatus("H"));
Produces a compiler error:
"Incompatible types. Required List but 'map' was inferred to Stream: no instance(s) of type variable(s) R exist so that Stream conforms to List"
Any ideas where I'm going awry?
Use forEach:
orders.forEach(order -> order.setStatus("H"));
You do not want to use Stream.map() because it requires a return value which replaces the original value in the stream. You are also missing a terminal operation in your stream, so even if you fix that by returning the original value it wont work. Stream.forEach() is a terminal operation you can use for this.
To update each object in your list you can just use orders.forEach(). This is the same as orders.stream().forEach().
orders.forEach(o -> o.setStatus("H"));
If you want to update only some values of your List you can use Stream.filter() before:
orders.stream()
.filter(o -> "ABC".equals(o.getName())
.forEach(o -> o.setStatus("H"));
I have a collection of objects, with the following type:
{
String action_name; //add or delete
long action_time;
String action_target;
}
Need to get the latest merged operation on each action_target
Sample input data:
[add|1001|item1, add|1002|item2, delete|1003|item1, add|1004|item1]
Expected result:
[add|1002|item2, add|1004|item1]
Sample input data:
[add|1001|item1, add|1002|item2, delete|1003|item1]
Expected result:
[add|1002|item2]
Sample input data:
[delete|1001|item1, add|1002|item2, add|1003|item1]
Expected result:
[add|1002|item2, add|1003|item1]
Is this approachable using Java8 stream APIs? Thanks.
You want to group by one criteria (the action_target) combined with reducing the groups to the maximum of their action_time values:
Map<String,Item> map=items.stream().collect(
Collectors.groupingBy(item->item.action_target,
Collectors.collectingAndThen(
Collectors.maxBy(Comparator.comparing(item->item.action_time)),
Optional::get)));
This returns a Map<String,Item> but, of course, you may call values() on it to get a collection of items.
Beautified with static imports, the code looks like:
Map<String,Item> map=items.stream().collect(groupingBy(item->item.action_target,
collectingAndThen(maxBy(comparing(item->item.action_time)), Optional::get)));
Your additional request of taking care of idempotent "add" and follow-up "delete" actions can be simplified to “remove items whose last action is "delete"” which can be implemented just by doing that after collecting using a mutable map:
HashMap<String,Item> map=items.stream().collect(groupingBy(
item->item.action_target, HashMap::new,
collectingAndThen(maxBy(comparing(item->item.action_time)), Optional::get)));
map.values().removeIf(item->item.action_name.equals("delete"));
I have a plain hashmap with numeric values and would like to retrieve its content, ideally in a list (but that can be worked out).
Can it be done?
Try this:
library(rJava)
.jinit()
# create a hash map
hm<-.jnew("java/util/HashMap")
# using jrcall instead of jcall, since jrcall uses reflection to get types
.jrcall(hm,"put","one", "1")
.jrcall(hm,"put","two","2")
.jrcall(hm,"put","three", "3")
# convert to R list
keySet<-.jrcall(hm,"keySet")
an_iter<-.jrcall(keySet,"iterator")
aList <- list()
while(.jrcall(an_iter,"hasNext")){
key <- .jrcall(an_iter,"next");
aList[[key]] <- .jrcall(hm,"get",key)
}
Note that using .jrcall is less efficient than .jcall. But for the life of me I can not get the method signature right with .jcall. I wonder if it has something to do with the lack of generics.
I have never done this myself, but there is an example in the rJava documentation of creating and working with a HashMap using the with function:
HashMap <- J("java.util.HashMap")
with( HashMap, new( SimpleEntry, "key", "value" ) )
with( HashMap, SimpleEntry )