Modify objects while iterating 2 Lists using java stream - java

List<Account> list1 = new ArrayList<>();
List<Order> list2 = new ArrayList<>();
list1.stream().forEach(l1 -> list2.stream()
.forEach(l2 -> {
if (l1.getOrderId() == l2.getOrderId())
l1.setStatus(l2.getStatus());
}));
I was doing like this. It worked fine but now I have another situation where if orderId is not present in list2 set the status as "invalid" for that particular l1.
OrderId is unique in both the tables.
Hope this gives better understanding.

Edit Thanks for the edit of your question, it makes a lot more sense now. I think that this should do what you’re after:
list1.forEach(acc -> acc.setStatus(list2.stream()
.filter(o -> o.getOrderId() == acc.getOrderId())
.findAny()
.map(Order::getStatus)
.orElse("invalid")));
I am using Iterable.forEach() and then Collection.stream() and Optional.map() for a functional way of calculating the status to be set.

Related

Filtering values from a list in a java 8 stream

What we have is a list of objects of type Object, we might take them from a cache for example, so we want to iterate over that list with a lambda stream and after mapping an object in every iteration we want to see if attribute of that new class is present in a list of string values that we passed to the method.
Your solution is not optimised. Using List.contains will give you O(n*m) complexity. Use a HashSet instead:
public List<MyClass> getMyClassListByStates(List<String> states) {
Set<String> set = new HashSet<>(states);
return cache.getCacheByCacheNameList(CacheTypeConstants.MY_CLASS)
.stream()
.map(MyClass.class::cast)
.filter(myc -> set.contains(myc.getState()))
.collect(Collectors.toList());
}
This will run in O(max(n,m)) time instead of O(n*m).
This is my solution to that problem:
public List<MyClass> getMyClassListByStates(List<String> states) {
return cache.getCacheByCacheNameList(CacheTypeConstants.MY_CLASS)
.stream()
.map((myc) -> (MyClass) myc)
.filter(myc -> states.contains(myc.getState()))
.collect(Collectors.toList());
}
If someone has any other way to do it, please be free to comment, thx.

Best way to replace nested loop concatenate string using java stream

I have a list of names and a list of versions. I want to get all permutations which are constructed by concatenating the string from two lists. I am using two for loop to do this but I want to switch to a more functional style approach. Here is my solution:
List<String> names = new ArrayList<>();
List<String> versions = new ArrayList<>();
List<String> result = new ArrayList<>();
names.forEach(name -> versions.stream().map(version -> result.add(name.concat(version))));
Is there a better way to do it?
You are looking for the "Cartesian Product" of names and versions — basically the return set/list from the aforementioned sets/lists.
final Stream<List<String>> result = names.stream()
.flatMap(s1 -> versions.stream().flatMap(s2 -> Stream.of(Arrays.asList(s1, s2))));
result.forEach(System.out::println);
Keep in mind that operation is super expensive. Google's Guava have this implemented also under com.google.common.collect.Sets.cartesianProduct(s1, s2).
You should look forward to use flatMap while streaming over names and then performing map operation further correctly as:
List<String> result = names.stream() // for each name
.flatMap(name -> versions.stream() // for each version
.map(version -> name.concat(version))) // concat version to the name
.collect(Collectors.toList()); // collect all such names
Or a bit tidier:
final List<String> result = names.stream() // Stream the Names...
.flatMap(name -> versions.stream() // ...together with Versions.
.map (version -> name.concat(version))) // Combine Name+Version
.collect(Collectors.toList()); // & collect in List.

How to use java stream map inside java stream filter

I have 2 arrays and want to make a list of role.getRoleName() only with elements that are in both arrays using streams.
final List<String> roleNames = new ArrayList<>();
roleNames = Arrays.stream(roles).filter(role -> role.getRoleId()
== Arrays.stream(permissions).map(permission -> permission.getRoleId()));
when I write the above code I get
Operator '==' cannot be applied to 'int', 'java.util.stream.Stream'
I understand the error, but I don't know the solution of how to make the permissions stream in only permission.getRoleId integers.
There is no way to compare such incompatible types as int and Stream.
Judging from what you've shown, Stream#anyMatch might a good candidate.
roleNames = Arrays.stream(roles)
.map(Role::getRoleId)
.filter(id -> Arrays.stream(permissions).map(Role::getRoleId).anyMatch(p -> p.equals(id)))
.collect(Collectors.toList());
This part Arrays.stream(permissions).map(Role::getRoleId) may be pre-calculated and stored into a Set.
final Set<Integer> set = Arrays.stream(permissions)
.map(Role::getRoleId)
.collect(Collectors.toSet());
roleNames = Arrays.stream(roles)
.filter(role -> set.contains(role.getRoleId()))
.map(Role::getRoleName)
.collect(Collectors.toList());
What you can do is collect unique roleIds for the array of Permissions into a Set as a computed result and perform a contains check as you iterate through the array of Roles. This could be done as :
final Set<Integer> uniqueRoleForPermissions = Arrays.stream(permissions)
.map(Permission::getRoleId)
.collect(Collectors.toSet());
final List<String> roleNames = Arrays.stream(roles)
.filter(role -> uniqueRoleForPermissions.contains(role.getRoleId()))
.map(Role::getRoleName)
.collect(Collectors.toList());

Java nested Map/List - filter by list contents, returning parent object

I have two string arguments to my function - "Pizza", and "Chips". I'd like to use streams to return the author whose "foods" key has contents matching these two strings
List<String> collection = Arrays.asList("Pizza", "Chips");
private static List<Map<String, Object>> authors = Arrays.asList(
ImmutableMap.of("id", "author-1",
"firstName", "Adam",
"lastName", "Awldridge",
"foods", Arrays.asList("Pizza", "Chips")),
ImmutableMap.of("id", "author-2",
"firstName", "Bert",
"lastName", "Bruce",
"foods", Arrays.asList("Pizza", "Fish")),
... // other authors
);
This is my attempt with streams:
return authors
.stream()
.filter(authors.stream()
.flatMap(author -> author.get("foods"))
.findAny(queryFoods))
.findFirst().orElse(null);
I want to return the first author who's foods match that of my query. I think the main difficulty is organizing data - unfortunately I can't get the following casting to work.
.flatMap(author -> (List<String>) author.get("foods"))
Also, this might stream through the authors too many times (I should use .filter on the stream I just made with
authors.stream()
Here you cannot directly treat the value of foods key as a List. Its just an Object. So, first you need to do an instance of check and if it is an instance of List, then you can check whether it contains the values that are there in your collection.
Map<String,Object> firstAuthor = authors
.stream()
.filter(author -> {
Object foods = author.get("foods");
if(foods instanceof List) {
List foodsList = (List) foods;
return foodsList.containsAll(collection);
}
return false;
})
.findFirst().orElse(null);
OUTPUT:
{id=author-1, firstName=Adam, lastName=Awldridge, foods=[Pizza, Chips]}
The above code will give you the required author if it exists or else null.
[Here, I have assumed that you want to check whether the author has all the food items that are present in collection object created by you. If you want to check for only one of the items then you can use contains() method from java.util.List instead of containsAll() method. Also, you will have to iterate over the collection object to check for each item in collection.]
I would solve it by filtering in stream:
Map<String,Object> author = authors.stream()
.filter(a -> a.containsKey("foods"))
.filter(a -> a.get("foods") instanceof List)
.filter(a -> ((List) a.get("foods")).containsAll(collection))
.findFirst().orElse(null);
Maybe this is what you want?
authors
.stream()
.filter(a -> a.get("foods").stream().anyMatch(x -> "Pizza".equals(x)))
.findFirst().orElse(null);

Java 8 Stream filter using list as condition

Hello I'm beginner when it comes to Java 8 so please be patient for me :)
I have a method that returns custom list of objects. What I need to do: I have got a list of disabledPaymentTypesStrings - and I don't know how many elements it has got. How can I change my code in order to not write every condition like !paymentType.getName().equalsIgnoreCase(disabledPaymentTypesStrings.get(1))? I would like to have somehow my whole list "disabledPaymentTypesStrings" placed here as a condition but I have no idea how to do that. Please give me some hints or advices :)
private List<PaymentType> listOfPaymentTypesForChangePayment(OrderPaymentTypeParameters paymentTypeParameters) {
List<String> disabledPaymentTypesStrings = newArrayList(Splitter.on(COMMA).split(systemUtils.getChangePaymentTypeDisabled()));
return paymentTypeSelector.availablePaymentTypesForChangePayment(paymentTypeParameters).stream()
.filter(paymentType ->
!paymentType.getName().equalsIgnoreCase(disabledPaymentTypesStrings.get(0)) &&
!paymentType.getName().equalsIgnoreCase(disabledPaymentTypesStrings.get(1)) &&
!paymentType.getName().equalsIgnoreCase(disabledPaymentTypesStrings.get(2)))
.collect(toList());
}
A stream approach could consist in the filter() to stream the List of String and keep PaymentType elements where paymentType.getName() don't match with any elements of the List of String :
return paymentTypeSelector.availablePaymentTypesForChangePayment(paymentTypeParameters)
.stream()
.filter(paymentType -> disabledPaymentTypesStrings.stream()
.allMatch(ref -> !ref.equalsIgnoreCase(paymentType.getName())))
.collect(toList());
But you could also compare Strings by using the same case. For example lowercase. It will simplify the filtering.
You can convert the reference list elements to lowercase :
List<String> disabledPaymentTypesStrings = newArrayList(Splitter.on(COMMA).split(systemUtils.getChangePaymentTypeDisabled()))
.stream()
.map(String::toLowerCase)
.collect(toList());
And you can so use List.contains() in the filter() :
return paymentTypeSelector.availablePaymentTypesForChangePayment(paymentTypeParameters)
.stream()
.filter(paymentType -> !disabledPaymentTypesStrings.contains(paymentType.getName().toLowerCase()))
.collect(toList());
Note that for big lists, using a Set would be more efficient.
Use contains(). But you have to think about case sensitivity ignoring
private List<PaymentType> listOfPaymentTypesForChangePayment(OrderPaymentTypeParameters paymentTypeParameters) {
List<String> disabledPaymentTypesStrings = newArrayList(Splitter.on(COMMA).split(systemUtils.getChangePaymentTypeDisabled()));
return paymentTypeSelector.availablePaymentTypesForChangePayment(paymentTypeParameters).stream()
.filter(paymentType -> !disabledPaymentTypesStrings.contains(paymentType)
.collect(toList());
}
Both the steps need to have values in common case(either in uppercase or in lowercase, I preferred lowercase)
List<String> disabledPaymentTypesStringsLowerCase = newArrayList(Splitter.on(COMMA).split(systemUtils.getChangePaymentTypeDisabled()))
.stream()
.map(String::toLowerCase)
.collect(toList());
return paymentTypeSelector.availablePaymentTypesForChangePayment(paymentTypeParameters)
.stream()
.map(paymentType -> paymentType.getName())
.map(String::toLowerCase)
.filter(disabledPaymentTypesStrings::contains)
.collect(toList());
This code can further be refactored if paymentType class is known, assuming class of paymentType is PaymentType code would look like below,
return paymentTypeSelector.availablePaymentTypesForChangePayment(paymentTypeParameters)
.stream()
.map(PaymentType::getName)
.map(String::toLowerCase)
.filter(disabledPaymentTypesStrings::contains)
.collect(toList());

Categories