Create list with objects from another list using RxJava - java

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

Related

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

Break from the inner nested stream when no elements are passed through filter

Here is the example of code that I struggle with:
List<CategoryHolder> categories = ...
List<String> categoryNames = categoryIds.stream()
.map(id -> categories.stream()
.filter(category -> category.getId().equals(id))
.findFirst().get().getName())
.collect(Collectors.toList());
So I have a list of CategoryHolder objects consisting of category.id and category.name. I also have a list of category ids. I want to iterate through ids and for each id I want to iterate through the CategoryHolder list and when id from categoryIds list is matched with a CategoryHolder.id I want to return the category.name. So basically I want to map every value from categoryIds to its category.name.
So the problem is when no values are matched, filter doesn't pass any elements through and there is nothing to collect, so I would like to break from the current inner stream, and continue to the next id from categoryIds list. Is there a way to achieve this in stream API?
You can do like:
categories.stream()
.filter(categoryHolder -> categoryIds.stream()
.anyMatch(id->categoryHolder.getId().equals(id)))
.map(CategoryHolder::getName)
.collect(Collectors.toList());
or for better performance you can do:
Map<String,CategoryHolder> map = categories.stream()
.collect(Collectors.toMap(CategoryHolder::getId, Function.identity()));
List<String> names = categoryIds.stream()
.map(id -> map.get(id).getName())
.collect(Collectors.toList());
The problem is with your call to filter where you are doing .findFirst().get().getName()
This would fail in case empty Optional is returned by findFirst().
You can instead rewrite it as follows:
List<String> categoryNames =
categoryIds
.stream()
.map(id -> categories
.stream()
.filter(catgory -> catgory.getId().equals(id))
.findFirst())
.collect(
ArrayList::new,
(list, optional) -> {
optional.ifPresent(categoryHolder -> list.add(categoryHolder.name));
},
(list, list2) -> {}
);

Is there any way to filter List by field value of first object in this list?

I have huge list with logging objects and I need to take only first n entries based on first object field value (list.get(0).getId())
I do this way:
List<Event> list = asList(
new Event("id_1", "value_1" ...),
new Event("id_1", "value_2" ...),
new Event("id_1", "value_3" ...),
new Event("id_2", "value_4" ...),
new Event("id_2", "value_5" ...),
new Event("id_2", "value_6" ...));
String id = events.get(0).getId();
return list.stream()
.filter(event -> event.getId().equals(id))
.collect(toList());
And it works, but it looks like not true way. Can you give me an advice how to do this better?
If you want to avoid the initial access to the list (to obtain the Id of the first event), you can group the events by Id, and return the first group of events:
return list.stream()
.collect(Collectors.groupingBy(Event::getId,
LinkedHashMap::new,
Collectors.toList()))
.values()
.iterator()
.next();
P.S. if the list can be empty, you still have to check that .values().iterator().hasNext().

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.

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