Iterating using ForEach in Spring -boot - java

I'm trying to iterate over a repository of database table and once there, access one of the row to finally retrieve the information i need.
Thus I first went to my repository over all those elements in the database, applied findAll() method; then being there I used stream().foreach(), which eventually positioned me inside each item being able to retrieve any kind of information, like accessing its lists and other things.
But that throws an exception
Required type:Object
Provided:void
here is the function :
public ResponseEntity<Map<String, Object>> allshots(Authentication authentication) {
Map<String, Object> dto = new HashMap<>();
dto.put("player_shots_detail", playerCrabRepository.findAll().stream().forEach(playerCrab -> { playerCrab.getDiceCrabList().stream()
.map(diceCrab -> makeDiceCrabMiniDto(diceCrab)).collect(Collectors.toList());}));
return new ResponseEntity<>(dto, HttpStatus.CREATED);
}
Does that mean I should return something instead of void?
I appreciate any help, and thanks in advance

forEach return void but dto.put requird an object. try replace forEach with map
dto.put("player_shots_detail", playerCrabRepository.findAll().stream().map(playerCrab -> playerCrab.getDiceCrabList()).flatMap(diceCrab -> makeDiceCrabMiniDto(diceCrab)).collect(Collectors.toList()));

forEach a sentinel operation on Stream doesn't return anything as it's return type is void. Use collect instead. Your Map needs an object as value while your operation will return nothing due to forEach.
Java Docs for your reference. https://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html#forEach-java.util.function.Consumer-

Return type for foreach in java in void. You can use map and flatmap functions of streams for the above use case.

Error denotes that your function is type void which response you try to place as value in map.
In short foreach block return nothing. In such case you have to store response in following way.
Current code is :
List<T> data = playerCrabRepository.findAll().stream()
.foreach(playerCrab -> {
playerCrab.getDiceCrabList.stream()
.map(makeDirceCrabMiniDto(diceCrab))
.collect(Collectors.toList())
})
dto.put("player_shots_detail", data);
Use map instead of foreach and return them & collect it.
In such condition you have to store your result in list,
List<T> data = playerCrabRepository.findAll().stream()
.map(playerCrab -> {
return playerCrab.getDiceCrabList.stream()
.map(makeDirceCrabMiniDto(diceCrab))
.collect(Collectors.toList())
}).collect(Collectors.toList());
dto.put("player_shots_detail", data);

Related

Java Streams - Are you able to have multiple terminal operations (e.g. forEach)

I'm fairly new to Java and trying to learn how to use streams for easier code writing. If I can code like this:
Map<String, SomeConfig> temp = new HashMap<>();
resultStorage.forEach((key, value) -> key.getUsers().forEach(user -> {
if (!temp.containsKey(user.getMeta())) {
SomeConfig emailConfiguration = key
.withCheck1(masterAccountId)
.withCheck2(getClientTimezone())
.withCheck3(user.getMeta());
temp.put(user.getMeta(), emailConfiguration);
}
temp.get(user. getMeta()).getStreams().add(value);
}));
return new ArrayList<>(temp.values());
resultStorage declaration:
private Map< SomeConfig, byte[]> resultStorage = new ConcurrentHashMap<>();
getStreams is a getter on SomeConfig that returns a List<byte[]> as here:
private List<byte[]> attachmentStreams = new ArrayList<>();
public List<byte[]> getAttachmentStreams() {
return attachmentStreams;
}
My first attempt was something similar to this:
resultStorage.entrySet().stream()
.forEach(entry -> entry.getKey().getUsers().forEach(user -> {
}));
Are we able to use a forEach within one of the streams terminating operation, forEach? How would a stream benefit in this case as I saw documentation that it can significantly improve readability and performance of older pre-Java8 code?
Edit:
resultStorage holds a ConcurrentHashMap. It will contain Map<SomeConfig, byte[]> for email and attachments. Using another HashMap temp that is initially empty - we analyze resultStorage , see if temp contains a specific email key, and then put or add based on the existence of a user's email
The terminal operation of entrySet().stream().forEach(…) is entirely unrelated to the getUsers().forEach(…) call within the Consumer. So there’s no problem of “multiple terminal operations” here.
However, replacing the Map operation forEach((key, value) -> … with an entrySet() .stream() .forEach(entry -> …) rarely adds a benefit. So far, you’re not only made the code longer, you introduced the necessity to deal with a Map.Entry instead of just using key and value.
But you can simplify your operation by using a single computeIfAbsent instead of containsKey, put, and get:
resultStorage.forEach((key, value) -> key.getUsers().forEach(user ->
temp.computeIfAbsent(user.getMeta(), meta ->
key.withCheck1(masterAccountId).withCheck2(getClientTimezone()).withCheck3(meta))
.getStreams().add(value)));
Notes after the code.
Map<String, SomeConfig> temp = resultStorage.keySet()
.stream()
.flatMap(key -> key.getUsers()
.stream()
.map(user -> new AbstractMap.SimpleEntry(user, key)))
.collect(Collectors.toMap(e -> e.getKey().getMeta(),
e -> e.getValue()
.withCheck1(masterAccountId)
.withCheck2(getClientTimezone())
.withCheck3(e.getKey().getMeta())
resultStorage.keySet()
This returns Set<SomeConfig>.
stream()
This returns a stream where every element in the stream is an instance of SomeConfig.
.flatMap(key -> key.getUsers()
.stream()
.map(user -> new AbstractMap.SimpleEntry(user, key)))
Method flatMap() must return a Stream. The above code returns a Stream where every element is an instance of AbstractMap.SimpleEntry. The "entry" key is the user and the entry value is the key from resultStorage.
Finally I create a Map<String, SomeConfig> via [static] method toMap of class Collectors.
The first argument to method toMap is the key mapper, i.e. a method that extracts the [map] key from the AbstractMap.SimpleEntry. In your case this is the value returned by method getMeta() of the user – which is the key from AbstractMap.SimpleEntry, i.e. e.getKey() returns a user object.
The second argument to toMap is the value mapper. e.getValue() returns a SomeConfig object and the rest is your code, i.e. the withChecks.
There is no way I can test the above code because not only did you not post a minimal, reproducible example, you also did not post any sample data. Hence the above may be way off what you actually require.
Also note that the above code simply creates your Map<String, SomeConfig> temp. I could not understand the code in your question that processes that Map so I did not try to implement that part at all.

Java 8 stream and set attribute from call Boolean method

Please can you help me resolve this issue I am having when attempting to stream through an array list and call a setter based on a method which returns a Boolean.
Written as a for loop, it would look like this:-
for (final PersonDto person : personList) {
person.setUserCanEdit(userHasWriteRole(person));
}
private Boolean userHasWriteRole(final PersonDto person) {
return getUserRoles().contains(getReadRole());
}
I have tried a few variations with no success, along the following lines
final List<PersonDto> results = personList.stream().filter(a -> a.setUserCanEdit(this::userHasWriteRole)).collect(Collectors.toList());
... But it complains with
The target type of this expression must be a functional interface
I think I would go for:
personList.stream()
.filter(p -> userHasWriteRole(p))
.forEach(p -> p.setUserCanEdit(true));
I think this keeps the intent clear,
Since you are updating the objects in array list, you can use forEach
personList.forEach(person ->person.setUserCanEdit(userHasWriteRole(person)));
The filter() method is an intermediate operation of the Stream interface that allows us to filter elements of a stream that match a given Predicate. You can't update data in the filter.
Use forEach for this.
personList.stream().forEach(a -> a.setUserCanEdit(userHasWriteRole(a)));
And if you want to get in new arraylist make a copy of list and do this operations on new list
List<PersonDto> copy = new ArrayList<>(personList);
copy.stream().forEach(a -> a.setUserCanEdit(userHasWriteRole(a)));
If you want to have a list with filtered elements and do the setUserCanEdit on every elements in that list; you can do like this:
List<PersonDto> newList = personList.stream()
.filter(p -> userHasWriteRole(p))
.collect(Collectors.toList());
newList.forEach(p -> p.setUserCanEdit(true));

Getting Incompatible types error while trying to map a list

I have a FeeAccount list that I would like to fill. I want to use .stream.map() to get it done. What I've managed to do is to make a method that would map my list and return it. I've written this code using some other examples I have found online. My problem is that somehow it returns a list that is incompatible with List.
I am getting an 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
As I understand the problem is with the part where I use collect(Collectors.toList()). But I am not sure. I don't even clearly understand what the error message means.
Maybe someone can explain what am I doing wrong? Is it with the .stream.map()? Because I never used it before. Or maybe the problem is somewhere else.
Method(List<contract> contractList){
List<FeeAccount> feeAccounts = new ArrayList<>();
feeAccounts = contractList
.stream()
.map(contract -> {
List<Fee> monthlyFees=...;
return monthlyFees.stream()
.map(monthlyFee -> {
FeeAccount account = new FeeAccount();
account.setFeeCode(monthlyFee.getFeeCode());
account.setDebtorAccount(contract.getDebtorAccount());
return account;
}).collect(Collectors.toList());
});}
You have two nested map operations. The outer transforms a contract to a List<FeeAccount>, and the inner transforms a Fee to a FeeAccount.
Hence, your pipeline results in a Stream<List<FeeAccount>> without a terminal operation.
If you add a .collect(Collectors.toList()) in the end, you'll get a List<List<FeeAccount>>.
If you want to merge all those inner lists into a single output list, you should use flatMap.
To obtain a flat List:
List<FeeAccount> feeAccounts =
contractList.stream()
.flatMap(contract -> {
List<Fee> monthlyFees=...;
return monthlyFees.stream()
.map(monthlyFee -> {
FeeAccount account = new FeeAccount();
account.setFeeCode(monthlyFee.getFeeCode());
account.setDebtorAccount(contract.getDebtorAccount());
return account;
});
})
.collect(Collectors.toList();
map() is an intermediate operation in a stream pipeline (please look at Stream operations and pipelines), which means that it returns a stream.
feeAccounts = contractList
.stream()
.map(...) // result of this operation is Stream<<List<FeeAccount>>
and not a List<FeeAccount>
You are missing a terminal operation like .collect(Collectors.toList() :
List<FeeAccount> feeAccounts = contractList
.stream()
.flatMap(monthlyFees -> monthlyFees.stream()
.map(monthlyFee -> {
FeeAccount account = new FeeAccount();
account.setFeeCode(monthlyFee.getFeeCode());
account.setDebtorAccount(contract.getDebtorAccount());
return account;
})
.collect(Collectors.toList());
flatMap transforms Stream<Stream<FeeAccount>> into just Stream<FeeAccount>

prepare map using Observable Rx-java

Here i my sample code which returns list of JsonDocument from couchbase server.
Cluster cluster = CouchbaseCluster.create();
Bucket bucket = cluster.openBucket();
List<JsonDocument> foundDocs = Observable
.just("key1", "key2", "key3", "key4", "key5")
.flatMap(new Func1<String, Observable<JsonDocument>>() {
#Override
public Observable<JsonDocument> call(String id) {
return bucket.async().get(id);
}
})
.toList()
.toBlocking()
.single();
I want to return Map instead of List.My return type would be Map<String, JsonDocument>.
I tried with toMap method but it did not work for me.
You correctly mentioned in your comment that toMap needs a Function as its argument. This Function will be used to "extract" (or maybe "construct") the key under which each value will be entered into the map.
So, in your case you will need a Function<JsonDocument, String> that takes a JsonDocument as its input and somehow returns a String which you will later use to find the JsonDocument in the Map. What that String will be is something only you can answer - maybe there is some ID inside the JsonDocument?

How can I convert these streamed Map keys from Longs to Objects?

I've current got a method which looks like:
public Map<Long, List<ReferralDetailsDTO>> getWaiting() {
return referralDao.findAll()
.stream()
.map(ReferralDetailsDTO::new)
.collect(Collectors.groupingBy(ReferralDetailsDTO::getLocationId, Collectors.toList()));
}
}
It returns me a Map of location IDs to ReferralDetailsDTO objects. However, I'd like to swap out the location ID for the LocationDTO object.
I'd have naively imagined something like this might work:
public Map<Long, List<ReferralDetailsDTO>> getWaiting() {
return referralDao.findAll()
.stream()
.map(ReferralDetailsDTO::new)
.collect(Collectors.groupingBy(locationDao.findById(ReferralDetailsDTO::getLocationId), Collectors.toList()));
}
Obviously, I'm here because it doesn't - Java complains the findById method is expecting a Long value, not the method reference. Any suggestions for how I can neatly address this? Thanks in advance.
First of all, change the key type of the Map from Long to your relevant class (is it LocationDTO or some other class?)
Second of all, use a lambda expression instead of method reference for the lookup :
public Map<LocationDTO, List<ReferralDetailsDTO>> getWaiting() {
return referralDao.findAll()
.stream()
.map(ReferralDetailsDTO::new)
.collect(Collectors.groupingBy(r -> locationDao.findById(r.getLocationId()));
}

Categories

Resources