Stream of optional fields return values - java

I have a few Optional fields with String and Long values:
Optional<Long> id;
Optional<String> name;
Optional<String> lastname;
Optional<Long> number;
....
I would like to return List with contains all of the values. If e.g optional "name" is no present, should be stored empty String. Result of method should be List with values e.q: "1", "John", "", "5".
I made stream:
Stream fields = Stream.of(id, name, lastname, number);
But I have no idea what next.
Regards.

You can use:
List<String> list = Stream.of(id, name, lastname, number)
.map(op -> op.map(o -> o.toString()).orElse(""))
.collect(Collectors.toList());
On each optional in stream you will map it into it's String version using toString() from Object class and for null you will map it into empty String. Than, you will collect it into list.

You can use the map method of the stream to manipulate the content of your stream, something like the below code:
fields.map(field -> field.orElse("").toString());
The optional also has a map method that can be used when manipulating the stream:
fields.map(field -> field.map(x -> x.toString()).orElse(""));

This works :
List<String> result = fields.map(o -> o.orElse("")) // provide default value
.map(o -> o.toString()) // cast to String
.collect(Collectors.toList()); // collect into List
See it in action.

List<String> ls = Stream.of(a, b).filter(o -> o != null && !o.trim().equals("")).map(o -> o instanceof Long ? String.valueOf(o) : o).collect(Collectors.toList());
Create a stream
Add a filter which will keep the values if two condition are respected: they are not null and if it is true the string value is not empty (the trim() method permits to remove the empty chars which are on the left and the right of the value)
Use a map method in order to transform objects which are Long instances into String instances
Use a collect method in order to retrieve values

Related

Using Streams and filter in Java and matching with a varargs

I know that .contains needs a string. Varargs can be a String structure of more than one. Like String... role can be "user", "admin". Or String... role can be "user".
Whatever is passed into it will be used.
So I have a filter - where I am trying to see if the getter thats a type String contains the value found in the varargs...
String a = Arrays.toString(role);
System.out.print(a);
TypeOfObject roles = userProjectRoles.stream().filter(userProjectRole ->
userProjectRole.getRole().getName().equals(a)).findFirst().orElse(null);
a has brackets and it is not a string, but an Array sent as a string value.
Can you help me on how to fix this?
Convert input array to Set and use its contains() method
Set<String> roleSet = new HashSet<>(Arrays.asList(role));
TypeOfObject roles = userProjectRoles.stream()
.filter(userProjectRole -> roleSet.contains(userProjectRole.getRole().getName())
.findFirst()
.orElse(null);
You can use filter userProjectRoles with the anyMatch method to check if roles contains that role:
var result = userProjectRoles.stream()
.map(role -> role.getRole().getName())
.filter(roleName -> Arrays.stream(roles).anyMatch(role -> roleName.equals(role)))
.collect(Collectors.toList())
or
var result = userProjectRoles.stream()
.map(role -> role.getRole().getName())
.filter(roleName -> {
for (String role : roles) if (roleName.equals(role)) return true;
return false;
})
.collect(Collectors.toList())
This will give you all of the elements in userProjectRoles whose names are also in the roles array, if I understand your question correctly.
If it's the other way around and you want all the elements in the roles array that are also in userProjectRoles, you can do this:
List<String> roleNames =
roles.stream()
.map(role -> role.getRole().getName())
.collect(Collectors.toList());
var result = Arrays.stream(roles).stream()
.filter(roleNames::contains)
.collect(Collectors.toList())
I presume you're trying to get rid of [ and ] from Arrays#toString but it's unclear since there is no direct answer. If so, this is how.
It is worth noting that this is unsafe because the result of role could be multiple values and not a single one.
String a = Arrays.toString(role).replaceAll("\\[", "").replaceAll("]", "");
An alternative might be to check if the array of roles contains the role name. I.e;
TypeOfObject roles = userProjectRoles.stream()
.filter(userProjectRole -> Stream.of(a).anyMatch(roleName -> userProjectRole.getRole().getName().equals(roleName))
.findFirst()
.orElse(null);

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

Return string from lambda expression stream().filter()

I have something like this and I want to get a String as a result
List<Profile> profile;
String result = profile
.stream()
.filter(pro -> pro.getLastName().equals("test"))
.flatMap(pro -> pro.getCategory())
getCategory() should return an string but not sure what I have to use to return a string, I tried several things but any worked
Any idea?
Thanks
List<Profile> profile;
String result = profile.stream()
.filter(pro -> pro.getLastName().equals("test"))
.map(pro -> pro.getCategory())
.findFirst()
.orElse(null);
There are a few solutions depending on what you're trying to do. If you have a single profile you're targeting that you want to get the category of, you can use a findFirst or findAny to get the profile you want and then get the category from the resulting Optional.
Optional<String> result = profile.stream()
.filter(pro -> pro.getLastName().equals("test"))
.map(Profile::getCategory)
.findFirst(); // returns an Optional
Note that findFirst returns an Optional. It handles the possibility that you don't actually have any that meet your criteria in a way that you can handle gracefully.
Alternatively, if you're trying to concatenate the categories of all profiles with last name of "test", then you can use a .collect(Collectors.joining()) to accumulate a String.
List<Profile> profile; // contains multiple profiles with last name of "test", potentially
String result = profile.stream()
.filter( pro -> pro.getLastName().equals("test"))
.map(Profile::getCategory)
.collect(Collectors.joining(", ")); // results in a comma-separated list
You can use on your stream method collect(Collectors.joining()) which will collect your Stream as String.
Under the hood it will use StringJoiner class:
https://docs.oracle.com/javase/8/docs/api/java/util/StringJoiner.html
Collectors class java doc:
https://docs.oracle.com/javase/8/docs/api/java/util/stream/Collectors.html#joining--
I think it will help you

Using map in pair with orElse in Java 8

Having to two different collection of objects say Set say A and List say B.
If set contains any object need to get the firstobject a string variable say objectA.getName() or else need to get the firstobject in List ObjectB.getPerson().getName() and if both the collection is empty null should be assigned to the string.
Below is my code:
Optional<String> name1 =a.stream().findfirst().map(P->p.getName());
String output = null;
if (name1.ispresent()) {
output = name1.get();
} else {
Optional<String> name2 =a.stream().findfirst().map(p1->p1.getPerson().getName());
if (name2.ispresent()) {
output = name2.get();
}
}
Here I am getting the correct value. Please help me to attain same using map in pair with orElse.
What about this?
Set<String> a = ...
List<String> b = ...
String output = a.stream()
.findFirst()
.map(p -> p.getName())
.orElseGet(() -> b.stream()
.findFirst()
.map(p1 -> p1.getPerson().getName())
.orElse(null));
The important part is the orElseGet method on the Optional. It is evaluated lazily.

Return empty element from Java 8 map operation

Using Java 8 stream what is the best way to map a List<Integer> when you have no output for the input Integer ?
Simply return null? But now my output list size will be smaller than my input size...
List<Integer> input = Arrays.asList(0,1,2,3);
List<Integer> output = input.stream()
.map(i -> {
Integer out = crazyFunction(i);
if(out == null || out.equals(0))
return null;
return Optional.of(out);
})
.collect(Collectors.toList());
I don’t get why you (and all answers) make it so complicated. You have a mapping operation and a filtering operation. So the easiest way is to just apply these operation one after another. And unless your method already returns an Optional, there is no need to deal with Optional.
input.stream().map(i -> crazyFunction(i))
.filter(out -> out!=null && !out.equals(0))
.collect(Collectors.toList());
It may be simplified to
input.stream().map(context::crazyFunction)
.filter(out -> out!=null && !out.equals(0))
.collect(Collectors.toList());
But you seem to have a more theoretical question about what kind of List to generate, one with placeholders for absent values or one with a different size than the input list.
The simple answer is: don’t generate a list. A List is not an end in itself so you should consider for what kind of operation you need this list (or its contents) and apply the operation right as the terminal operation of the stream. Then you have your answer as the operation dictates whether absent values should be filtered out or represented by a special value (and what value that has to be).
It might be a different answer for different operations…
Replace the map call with flatMap. The map operation produces one output value per input value, whereas the flatMap operation produces any number of output values per input value -- include zero.
The most straightforward way is probably to replace the check like so:
List<Integer> output = input.stream()
.flatMap(i -> {
Integer out = crazyFunction(i);
if (out == null || out.equals(0))
return Stream.empty();
else
return Stream.of(out);
})
.collect(Collectors.toList());
A further refactoring could change crazyFunction to have it return an Optional (probably OptionalInt). If you call it from map, the result is a Stream<OptionalInt>. Then you need to flatMap that stream to remove the empty optionals:
List<Integer> output = input.stream()
.map(this::crazyFunctionReturningOptionalInt)
.flatMap(o -> o.isPresent() ? Stream.of(o.getAsInt()) : Stream.empty())
.collect(toList());
The result of the flatMap is a Stream<Integer> which boxes up the ints, but this is OK since you're going to send them into a List. If you weren't going to box the int values into a List, you could convert the Stream<OptionalInt> to an IntStream using the following:
flatMapToInt(o -> o.isPresent() ? IntStream.of(o.getAsInt()) : IntStream.empty())
For further discussion of dealing with streams of optionals, see this question and its answers.
Simpler variants of #Martin Magakian 's answer:
List<Integer> input = Arrays.asList(0,1,2,3);
List<Optional<Integer>> output =
input.stream()
.map(i -> crazyFunction(i)) // you can also use a method reference here
.map(Optional::ofNullable) // returns empty optional
// if original value is null
.map(optional -> optional.filter(out -> !out.equals(0))) // return empty optional
// if captured value is zero
.collect(Collectors.toList())
;
List<Integer> outputClean =
output.stream()
.filter(Optional::isPresent)
.map(Optional::get)
.collect(Collectors.toList())
;
You can wrap the output into an Optional which may or may not contain a non-null value.
With an output: return Optional.of(out);
Without output: return Optional.<Integer>empty();
You have to wrap into an option because an array cannot contain any null value.
List<Integer> input = Arrays.asList(0,1,2,3);
List<Option<Integer>> output = input.stream()
.map(i -> {
Integer out = crazyFunction(i);
if(out == null || out.equals(0))
return Optional.<Integer>empty();
return Optional.of(out);
})
.collect(Collectors.toList());
This will make sure input.size() == output.size().
Later on you can exclude the empty Optional using:
List<Integer> outputClean = output.stream()
.filter(Optional::isPresent)
.map(i -> {
return i.get();
})
.collect(Collectors.toList());

Categories