I'm new to Java 8 Streams and I'm currently trying to convert a for loop into a java 8 stream. Could I get some help?
for (Subscription sub : sellerSubscriptions) {
if (orders.get(Product).test(sub)) {
orderableSubscriptions.add(sub.getId());
}
}
sellerSubscriptions = List.
orders = Map<String,Predicate<Subscription>>
orderableSubscriptions = Set<String>
Create a Stream of Subscriptions via the Collection#stream() method
Use of the Stream#filter() method to "simulate" the if statement, by filtering out all subscription that don't pass the given predicate.
By using the Stream#map() method you convert your stream of subscriptions, to a stream of ids
Finally by using the Stream#collect() you can collect the stream into anything you'd like. E.g. a Set
Your code could look like this:
Set<String> ids = sellerSubscriptions.stream() // create a Stream<Subscription>
.filter(orders.get(Product)::test) // filter out everthing that doesn't match
.map(Subscription::getId) // only use the ids from now on
.collect(Collectors.toSet()); // create a new Set from the elements
Some notes:
Subscription::getId (a method reference) is functionally equal to the lambda sub -> sub.getId()
orders.get(Product)::test (also a method reference) retrieves the predicate only once. As it seems to be the same predicate for all your subscriptions
Though it is not equal to sub -> orders.get(Product).test(sub) as that would invoke orders.get(Product) for every element
Related
I have the following list:
val tidslinjehendelser = kontaktPage.tidslinjeCard().getTidslinjehendelser()
fun getTidslinjehendelser(): MutableList<TidslinjehendelseElement> {
return Getters.getElementsInElement(element, By.tagName("tidslinje-hendelse")).stream()
.map { el: WebElement? -> TidslinjehendelseElement(el) }.collect(Collectors.toList())
}
Which is created from this class:
class TidslinjehendelseElement(private val element: WebElement?) {
fun dato(): String {
return getElementInElement(element, By.cssSelector("[data-e2e-selector=tidslinje-hendelse-dato]")).text
}
fun navn(): String { return getElementInElement(element, By.className("hendelse-navn")).text }
fun innhold(): String { return getElementInElement(element, By.className("hendelse-body")).text }
}
What I want to do, is to search for an element where the innhold() text contains a certain text string.
Right now, I'm doing it with a loop:
for (hendelse in tidslinjehendelser) {
if (hendelse.innhold().contains(melding)) {
print("yay!")
}
}
But since I've just started looking at streams, I'd like to explore using that method instead of the looping method.
You can use streams to filter the tidslinjehendelser list to get only the elements whose innhold() method returns a string containing a certain text string. Here's an example:
tidslinjehendelser.stream()
.filter { hendelse -> hendelse.innhold().contains(melding) }
.forEach { hendelse -> println("yay!") }
This code uses the stream() method to convert the tidslinjehendelser list to a stream. Then, it uses the filter() method to filter the stream and keep only the elements whose innhold() method returns a string containing the melding text. Finally, it uses the forEach() method to loop over the filtered stream and print "yay!" for each matching element.
You can replace the println("yay!") statement with any other code you want to execute for the matching elements. Also note that this code will stop iterating over the stream once it finds the first matching element. If you want to find all the matching elements, you can use the collect() method instead of forEach() to collect the matching elements into a new list or other collection.
I am writing here to find string from the list what I have understand.
List<String> result = list.stream()
.filter(x -> x.contains("string")
.collect(Collectors.toList());
Lets keep posted if you need more help
The trick is that this object MediaContainerModel inherits equals(Object) directly from Object and I can't and don't want to override equals in its class definition. This is what I have at the moment:
private void addMediaContainer(MediaContainerModel newMediaContainer, ProductModel product) {
List<MediaContainerModel> galleryImages = new ArrayList<>(product.getGalleryImages());
MediaContainerModel inserted = null;
// if a MediaContainer with same qualifier as newMediaContainer already exists, replace it
// with the new one to prevent duplicates
for (int i = 0; i < galleryImages.size(); i++) {
if (galleryImages.get(i).getQualifier().equals(newMediaContainer.getQualifier())) {
inserted = galleryImages.set(i, newMediaContainer);
}
}
// if not, add it
if (inserted == null) {
galleryImages.add(newMediaContainer);
galleryImages.sort((image1, image2) -> image2.getQualifier().compareTo(image1.getQualifier()));
}
product.setGalleryImages(galleryImages);
}
I want to do the same thing without the ugly for-loop by overriding MediaContainerModel.equals(Object) for this method only so I can use List.indexOf(Object) or something with lambdas. Is this possible in Java? If so how? Thanks!
without using a loop
I bet you are looking for the java-stream way:
List<MediaContainerModel> galleryImages = new ArrayList<>(product.getGalleryImages());
galleryImages.stream()
.filter(image -> newMediaContainer.getQualifier() // filter the equal ones
.equals(image.getQualifier()))
.findAny() // find any existing
.ifPresent(image -> { // add if present
galleryImages.add(newMediaContainer);
galleryImages.sort(Comparator.comparing(MediaContainerModel::getQualifier));
});
product.setGalleryImages(galleryImages);
Few notes:
The filtering uses exhaustive iteration as well as for-loop which means that all elements are iterated and multiple equal MediaContainerModel objects with same qualifiers. That's fine as long as you want to find if there is any qualified (findAny). Otherwise, to find the last one, you have to replace the line with:
.reduce((first, second) -> second)
The result using Java Stream API is a bit clumsy. I see you insert a new element and sort the list which means your intention is to keep the list always sorted. If there are no duplicate values allowed, I recommend using rather TreeSet which keeps the elements sorted upon addition or deletion. The whole solution would be the way easier:
Set<MediaContainerModel> galleryImages = new TreeSet<>(Comparator.comparing(MediaContainerModel::getQualifier));
galleryImages.addAll(product.getGalleryImages());
galleryImages.add(newMediaContainer); // won't be added if is already present
product.setGalleryImages(new ArrayList<>(galleryImages));
... if the ProductModel uses Collection or Set over List, then the last line is more straightforward:
product.setGalleryImages(galleryImages);
I have transformed a regular for loop code into java 8 streams. I tried a few, but i am still learning this and running out of ideas, please suggest ideas. can this be further simplified ? Other than using forEach, I am not able to change much.
Also, why do I have to typecast the eid to String in getERecordFromId((String)eid)
Stream <String>eIdsStream = getEidStream();
final HashSet<String> declinedRecords = new HashSet<>();
eIdsStream.forEach (eid -> {
ERecord eRecord = getERecordFromId((String)eid);
if(eRecord.getEHash() != null && Status.DECLINED == eRecord.getStatus()) {
declineRecords.add(eRecord.getEHash());
}
}
The casting is required since you use a raw Stream variable. Assuming getEidStream() returns a Stream<String>, you should have assigned it to a Stream<String> variable, or not assigned it to a variable at all.
Using forEach defeats the purpose of using Streams in the first place.
You should use filter and map to transform the Stream to hold the required elements, and then collect to a Set.
Set<String> declinedRecords =
getEidStream().map(eid -> getERecordFromId(eid))
.filter(eRecord -> eRecord.getEHash() != null && Status.DECLINED == eRecord.getStatus())
.map(ERecord::getEHash)
.collect(Collectors.toSet());
I have a list of Observables (RxJava 1).
List<Observable> observableList = new ArrayList<>();
It can contain at least 1 Observable. Each has the same type of the result.
How can I zip results of the all Observables?
I thought about zip-operator but it doesn't support List and I don't know quantity of observables (it can be 1,2,3,4....)
You can use the static zip(java.lang.Iterable<? extends Observable<?>> ws,FuncN<? extends R> zipFunction) method.
It is a zip method that takes an Iterable of Observables and a FuncN (which takes a varargs parameter for its call method) and uses it to combine the corresponding emitted Objects into the result to be omitted by the new returned Observable.
So for example you could do:
Observable.zip(observableList, new FuncN(){
public ReturnType call(java.lang.Object... args){
ReturnType result; //to be made
//preparatory code for using the args
for (Object obj : args){
ReturnType retObj = (ReturnType)obj;
//code to use the arg once at a time to combine N of them into one.
}
return result;
}
});
ReactiveX - Zip operator
Zip beyond BiFunction
Zip combine the emissions of multiple Observables together via a
specified function and emit single items for each combination based on
the results of this function
Here, list is an Array List of Observables of whichever type you want to pass.
val list = arrayListOf<Observable<ImageUrlResponse>>()
Observable.zip(list) { args -> Arrays.asList(args) }
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe({
val response = it[0]
val imageUrlResponse = imageUrlObject as ImageUrlResponse
urls.add(imageUrlResponse.imageUrl)}
}, {
val c = it
})
The Result of the following subscription is this image below. Just like we expect it to be zipped together. Also can you notice it returns all the responses to be zipped in a single java.lang.Object[].
Note I had to type cast my Array List to access my single object because it is of type Any!
I struggled with this as well, and used Sharan's solution as a base for mine.
My use case was doing API calls to several 3rd party providers, and then putting each individual result in a List. Each item in the list contains what the API returned, be it success or failure.
In the end it actually looks quite elegant. In my specific case "ResultType" was replaced with something like "ApiGenericResponseObject".
Observable.zip(listOfObservables, args -> {
List<ResultType> result = new ArrayList<>();
for (Object o: args) {
ResultType c = (ResultType) o;
// additional code here, I am just concatenating them together
// This gives me a list with the individual result of each Observable (for instance an API call)
result.add(c);
}
return result;
});
Alternatively, as a Lambda it looks neater. Though I wonder whether someone reading this will understand what is going on:
Observable.zip(listOfObservables, args -> Arrays.stream(args)
.map(object -> (ResultType) object)
.collect(Collectors.toList())
);
Hope it helps!
Suppose there is a list say
List<String> myList = new ArrayList<String>();
myList.add("okay");
myList.add("omg");
myList.add("kk");
I am doing this:
List<String> fianllist = myStream.map(item -> item.toUpperCase()).filter(item
->item.startsWith("O")).collect(Collectors.toList());
My question is what the difference between map and filter as both can take a lambda expression as a parameter. Can some one please explain?
By using map, you transform the object values.
The map operation allows us to apply a function, that takes in a parameter of one type, and returns something else.
Filter is used for filtering the data, it always returns the boolean value.
If it returns true, the item is added to list else it is filtered out (ignored)
for eg :
List<Person> persons = …
Stream<Person> personsOver18 = persons.stream().filter(p -> p.getAge() > 18);
For more details on this topic you can visit this link
map returns a stream consisting of the results of applying the given function to the elements of this stream. In a simple sentence, the map returns the transformed object value.
For example, transform the given Strings from Lower case to Upper case by using the map().
myList.stream().map(s -> s.toUpperCase()).forEach(System.out::println);
filter returns a stream consisting of the elements of this stream that match the given predicate. In a simple sentence, the filter returns the stream of elements that satisfies the predicate.
For example, find the strings which are starting with 'o' by using the filter.
myList.stream().filter(s -> s.startsWith("o")).forEach(System.out::println);
Program:
import java.util.ArrayList;
import java.util.List;
public class MapAndFilterExample {
static List<String> myList = null;
static {
myList = new ArrayList<String>();
myList.add("okay");
myList.add("omg");
myList.add("kk");
}
public static void main(String[] args) {
System.out.println("Converting the given Strings from Lower case to Upper case by using map");
myList.stream().map(s -> s.toUpperCase()).forEach(System.out::println);
System.out.println("filter which is starting with 'o' by using filter");
myList.stream().filter(s -> s.startsWith("o")).forEach(System.out::println);
}
}
Filter takes a predicate as an argument so basically you are validating your input/collection against a condition, whereas a map allows you to define or use a existing function on the stream eg you can apply String.toUpperCase(...) etc. and transform your inputlist accordingly.