Java 8 stream and set attribute from call Boolean method - java

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));

Related

how can I flatMap a stream that has a map returning a List?

I havent done something like this and I am not really sure why its not working.
SO I have something like this:
List<String> names = students.stream()
.map(StudentService::getNamesCode) // this returns a String
.flatMap(value -> StudentService::getInfo) // this returns a List<String> for each entry
.map(// do something else)
.distinct()
.collect(Collectors.toList());
Is it possible? I am pretty sure Im doing something wrong, on the flatMap I should send a streamable element, in this case a list should do the job.
I also tried Stream.of(StudentService::getInfo) but no luck.
Any idea?
The way to do it is convert the list to a stream. But the problem of doing like this Stream.of(StudentService::getInfo) is that you are creating a stream with a single element that is a list.
You should use instead StudentService.getInfo(value).stream() so you convert the list to a stream of elements.
Your final code would be:
List<String> names = students.stream()
.map(StudentService::getNamesCode) // this returns a String
.flatMap(value -> StudentService.getInfo(value).stream()) // this returns a List<String> for each entry
.map(// do something else)
.distinct()
.collect(Collectors.toList());

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>

Filter based on condition and collect the object

In java 8, collect emp object based on some filter condition.
In main class:
List<Emp> empList = Arrays.asList(
new Emp("aaa", language1),
new Emp("cc", language2),
new Emp("bb", language3),
new Emp("dd", language3)
);
empList.stream()
.flatMap(s->s.getLanguage().stream())
.filter(s->s.equals("java"))
.forEach(System.out::println); //Here just i am printing.
Actually I need to collect new List<EMP>.
How to collect emp object that are all have language "java". How can I do this?
You should not use flatMap if you want to collect Emp objects in the end because it will change every element to something else and it can be quite hard to map them back.
You should put all your logic in a filter: "keep the Emp object if getLanguage contains "java"".
empList.stream()
.filter(x->x.getLanguage().contains("java"))
.collect(Collectors.toList());
You can also do like this:
List<Object> optionMetas = new ArrayList<>();
Map<Long, Object> optionIdMetaMap_ = optionMetas.stream()
.filter(option -> option.getXX() || option.getXXX().equal("java"))
.collect(Collectors.toMap(Object::getKEY, item -> item));
Add your relevant condition in filter().
You can process the current list using removeIf method that accepts a filter predicate:
empList.removeIf(e -> !e.getLanguage().equals("java"));
Or you can copy the current list to another list and do the same.

Create list with objects from another list using RxJava

I have a method: Observable<List<String>> getNames();
I have object Person with constructors Person() and Person(String name)
Also there is a empty list ArrayList<Person> cachedPersons.
What I need:
In my method using RxJava fill array with Person using constructor<String> from List<String> like this:
ArrayList<String> cachedPersons = new ArrayList<Person>();
Observable<List<String>> getNames(){
Observable.from(getNames())
.map{
//cachedPersons.addAll(/*add new Person with corresponding String*/)
//with List("John","Sara","David")
//create cachedPersons = List(Person1("John"),Person2("Sara"),Person3("David"))
}
}
Observable.from(getNames())
.map{ name -> new Person(name)}
.toList()
.subscribe(
list -> cachedPersons.addAll(list)
);
The problem is that you are trying to fill needed ArrayList from map() method, and it is wrong.
It will never be executed until you make a subscription. You are not doing it in your code, so your ArrayList will not be filled with above code.
Do subscription and in subscription onNext you can fill your ArrayList.
Maybe this
Observable.from(getNames())
.doOnNext(name -> cachedPersons.add(new Person(name)))
.toList()
.subscribe();
This way you are just filling it without changing the stream value.
Observable<SourceObjet> source = ...// get first list from here
source.flatMapIterable(list -> list)
.map(item -> new ResultsObject().convertFromSource(item))
.toList()
.subscribe(transformedList -> ...);
If your Observable emits a List, you can use these operators:
flatMapIterable -> transform your list to an Observable of items
map -> transform your item to another item
toList -> transform a completed Observable to a Observable which emit a list of items from the completed Observable

Emit one List item at a time from a Flowable

I have a method which returns a Flowable<RealmResults<MyClass>>. For those not familiar with Realm, RealmResults is just a simple List of items.
Given a Flowable<RealmResults<MyClass>>, I'd like to emit each MyClass item so that I can perform a map() operation on each item.
I am looking for something like the following:
getItems() // returns a Flowable<RealmResults<MyClass>>
.emitOneAtATime() // Example operator
.map(obj -> obj + "")
// etc
What operator will emit each List item sequentially?
You would flatMap(aList -> Flowable.fromIterable(aList)). Then you can map() on each individual item. There is toList() if you want to recollect the items (note: this would be a new List instance). Here's an example illustrating how you can use these methods to get the different types using List<Integer>.
List<Integer> integerList = new ArrayList<>();
Flowable<Integer> intergerListFlowable =
Flowable
.just(integerList)//emits the list
.flatMap(list -> Flowable.fromIterable(list))//emits one by one
.map(integer -> integer + 1);
The question is, do you want to keep the results as a Flowable<List<MyClass>> or as a Flowable<MyClass> with retained order?
If the first,
getItems()
.concatMap(results -> Flowable
.fromIterable(results)
.map(/* do your mapping */)
.toList()
)
If the second, this should suffice:
getItems()
.concatMap(Flowable::fromIterable)
.map(/* do your mapping */)

Categories

Resources