I'm pretty new to Java streams. I've to split a string returned by filter in stream, create a new object with the strings in the split and compare each object with a predefined object. Stream looks like this (I know this is incorrect, just a representation of what I am trying to do):
xmlstream.stream()
.filter(xml->xml.getName()) //returns a string
.map(returnedString -> split("__"))
.map(eachStringInList -> new TestObj(returnedStr[0], returnedStr[1]))
.map(eachTestObj -> eachTestObj.compareTo(givenObj))
.max(Comparing.compare(returnedObj :: aProperty))
How do I achieve this? Basically map each string in list to create an object, compare that to a fix object and return max based on one of the properties.
Thanks.
You could use reduce like so:
TestObj predefined = ...
TestObj max =
xmlstream.stream()
.map(xml -> xml.getName()) //returns a string
.map(s -> s.split("__"))
.map(a -> new TestObj(a[0], a[1]))
.reduce(predifined, (e, a) ->
e.aProperty().compareTo(a.aProperty()) >= 0 ? e : a);
A more efficient version of the above would be:
TestObj predefined = ...
TestObj max =
xmlstream.stream()
.map(xml -> xml.getName()) //returns a string
.map(s -> s.split("__"))
.map(a -> new TestObj(a[0], a[1]))
.filter(e -> e.aProperty().compareTo(predefined.aProperty()) > 0)
.findFirst()
.orElse(predefined);
Update:
if you want to retrieve the max object by a given property from all the TestObj objects less than the predefined TestObj, then you can proceed as follows:
TestObj predefined = ...
Optional<TestObj> max =
xmlstream.stream()
.map(xml -> xml.getName())
.map(s -> s.split("_"))
.map(a -> new TestObj(a[0], a[1]))
.filter(e -> e.aProperty().compareTo(predefined.aProperty()) < 0)
.max(Comparator.comparing(TestObj::aProperty));
max returns an Optional<T>; if you're unfamiliar with it then consult the documentation here to familiarise you're with the different ways to unwrap an Optional<T> object.
Related
I have following code where I want to get value for multiple keys which starts with same name:
for example contents_of_a1, contents_of_ab2, contents_of_abc3
Optional.ofNullable(((Map<?, ?>) fieldValue))
.filter(Objects::nonNull)
.map(coverages -> coverages.get("contents_of_%"))
.filter(Objects::nonNull)
.filter(LinkedHashMap.class::isInstance)
.map(LinkedHashMap.class::cast)
.map(contents -> contents.get("limit"))
.map(limit -> new BigDecimal(String.valueOf(limit)))
.orElse(new BigDecimal(number));
How can I pass contents_of%
I don't know the reasons behind the data structure and what you want to achieve.
However, it is not important as this can be easily reproduced.
Using of Optional is a good start, however, for iterating and processing multiple inputs, you need to use java-stream instead and then Optional inside of collecting (I assume you want Map<String, BigDecimal output, but it can be adjusted easily).
Also, note .filter(Objects::nonNull) is meaningless as Optional handles null internally and is never passed to the next method.
final Map<String, Map<?, ?>> fieldValue = Map.of(
"contents_of_a", new LinkedHashMap<>(Map.of("limit", "10")),
"contents_of_b", new HashMap<>(Map.of("limit", "11")), // Different
"contents_of_c", new LinkedHashMap<>(Map.of("amount", "12")), // No amount
"contents_of_d", new LinkedHashMap<>(Map.of("limit", "13")));
final List<String> contents = List.of(
"contents_of_a",
"contents_of_b",
"contents_of_c",
// d is missing, e is requested instead
"contents_of_e");
final int number = -1;
final Map<String, BigDecimal> resultMap = contents.stream()
.collect(Collectors.toMap(
Function.identity(), // key
content -> Optional.of(fieldValue) // value
.map(coverages -> fieldValue.get(content))
.filter(LinkedHashMap.class::isInstance)
// casting here to LinkedHashMap is not required
// unless its specific methods are to be used
// but we only get a value using Map#get
.map(map -> map.get("limit"))
.map(String::valueOf)
.map(BigDecimal::new)
// prefer this over orElse as Optional#orElseGet
// does not create an object if not required
.orElseGet(() -> new BigDecimal(number))));
// check out the output below the code
resultMap.forEach((k, v) -> System.out.println(k + " -> " + v));
Only the content for a is used as the remaining were either not an instance of LinkedHashMap, didn't contain a key limit or were not among requested contents.
contents_of_a -> 10
contents_of_b -> -1
contents_of_e -> -1
contents_of_c -> -1
If you want to filter a map for which key starting with "contents_of_", you can do this for Java 8:
Map<String, Object> filteredFieldValue = fieldValue.entrySet().stream().filter(e -> {
String k = e.getKey();
return Stream.of("contents_of_").anyMatch(k::startsWith);
}).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
inscTipoTrabAval.getInscricaoTipoTrabalhoAvaliadorQuestoes()
.stream()
.filter(aq -> aq.getEventoQuestao().equals(eventoQuestao))
.findFirst()
.orElse(new InscricaoTipoTrabalhoAvaliadorQuestao(eventoQuestao, inscTipoTrabAval))
.setJustificativa(justificativa);
I'm trying to write an object into a list if it doesn't exist with orElse, but it isn't adding to the list. Is there any way to do this?
The easiest is to store the list in a variable, and check the content of your optional
//assuming the list is not immutable
List<InscricaoTipoTrabalhoAvaliadorQuestao> list = inscTipoTrabAval.getInscricaoTipoTrabalhoAvaliadorQuestoes();
list.stream()
.filter(aq -> aq.getEventoQuestao().equals(eventoQuestao))
.findFirst()
.ifPresentOrElse(
existing -> existing.setJustificativa(justificativa),
() -> {
var value = new InscricaoTipoTrabalhoAvaliadorQuestao(eventoQuestao, inscTipoTrabAval));
value.setJustificativa(justificativa);
list.add(value);
}
);
If you're on Java 8, you can use an if block
Optional<InscricaoTipoTrabalhoAvaliadorQuestao> value = list.stream()
.filter(aq -> aq.getEventoQuestao().equals(eventoQuestao))
.findFirst()
if(value.isPresent()) {
value.get().setJustificativa(justificativa);
} else {
InscricaoTipoTrabalhoAvaliadorQuestao newValue = new InscricaoTipoTrabalhoAvaliadorQuestao(eventoQuestao, inscTipoTrabAval));
newValue.setJustificativa(justificativa);
list.add(newValue);
}
Suppose I have a simple list:
List<String> listOne = Arrays.asList("str1", "result1", "test", "str4", "result2", "test", "str7", "str8");
The target is "test" and I want to add the value before the target into a new list, so the output would be [result1, result2].
It's easy enough to add the "test" values with something like listTwo = listOne.stream().filter(i -> i.equals("test")).collect(Collectors.toList()); but how can I get the values elsewhere based on the location of the target ( in my example it's just the element before the target )
I tried just changing the i to i - 1 but that did nothing.
I am aware I can do it with a simple for loop, but just wondering how to apply the same logic with a stream.
for (int i = 1; i < listOne.size(); i++) {
if (listOne.get(i).equals("test")) {
listTwo.add(listOne.get(i - 1));
}
}
To build on top of Naman's answer:
You can directly collect to a List<String>, which is more functional.
Also I would do the .equals test the other way in case one of the element of the list is null
Here you go:
List<String> listTwo = IntStream.range(1, listOne.size())
.filter(i -> "test".equals(listOne.get(i))) // stream of indexes of "test" elements
.mapToObj(i -> listOne.get(i-1)) // stream of elements at the index below
.collect(Collectors.toList());
Something like
IntStream.range(1, listOne.size())
.filter(i -> listOne.get(i).equals("test"))
.mapToObj(i -> listOne.get(i - 1))
.forEach(item -> listTwo.add(item));
Edit:
Basically I want to filter all the entries based on whether entry.getObject() is a string that contains value "value".
So I have a block of code that looks something like this:
list.stream()
.filter((entry) -> entry.getObject() != null)
.filter((entry) -> entry.getObject() instanceof String)
.filter((entry) -> ((String)entry.getObject()).toLowerCase().contains(value))
.collect(Collectors.toList());
The main problem is that I can't figure out how to structure this to persist the value of entry.getObject(), and I can't figure out how to manipulate the output of of entry.getObject() without losing sight of the entry that yielded the value. Earlier attempts looked more like this:
list.stream()
.map((entry) -> entry.getObject())
.filter((object) -> entry instanceof String)
.map((object) -> (String)entry)
.filter((str) -> str.toLowerCase().contains(value))
/* ... */
But then I couldn't figure out any way to relate it to the entry in the list that I started out with.
A possible solution might look like this:
list.stream()
.filter((entry) -> Arrays.stream(new Entry[] {entry})
// Map from entry to entry.getObject()
.map((entry) -> entry.getObject())
// Remove any objects that aren't strings
.filter((object) -> entry instanceof String)
// Map the object to a string
.map((object) -> (String)entry)
// Remove any strings that don't contain the value
.filter((str) -> str.toLowerCase().contains(value))
// If there is a product remaining, then entry is what I want
.count() > 0)
.collect(Collectors.toList());
This way, we can split off and analyze entry.getObject() without multiple calls to get the value back at each step.
You could do something like
list.stream()
.map( (e) -> new Entry(e, e.getObject()) )
.filter( (p) -> p.getValue() instanceof String )
//...
.map( (p) -> p.getKey() )
.collect(Collectors.toList());
Using Map.Entry or swing.Pair (or roll your own tuple like structure)
Having a list like the following one
List<Integer> values = new ArrayList<Integer>();
values.add(1);
values.add(0);
values.add(1);
values.add(1);
values.add(0);
I want to print the elements > 0 adding them a value, for example 10, by using Java 8 Stream. For example:
values.stream()
.filter(val -> val > 0)
// HERE add 10
.forEach(System.out::println);
Is it possible to do that? If yes, how?
Use the map operation
values.stream()
.filter(val -> val>0)
.map(x -> x+10)
.forEach(System.out::println);
If you need to keep the values, do
List<Integer> newValues = values.stream()
.filter(val -> val>0)
.map(x -> x+10)
.collect(Collectors.toList());
If you want to print and save new data in one go you can use peek() like below -
List<Integer> newValues = myList.stream().filter(val -> val>0).map(x -> x+10)
.peek(System.out::println).collect(Collectors.toList());
values = StreamSupport.stream(values)
.filter(val -> val > 0)
.forEach(val -> val += 10)
.collect(Collectors.toList());
then you can use:
values.stream()
.forEach(System.out::println);