Find Max of Multiple Lists - java

I'm pretty new to java streams and am trying to determine how to find the max from each list, in a list of lists, and end with a single list that contains the max from each sublist.
I can accomplish this by using a for loop and stream like so:
// databaseRecordsLists is a List<List<DatabaseRecord>>
List<DatabaseRecord> mostRecentRecords = new ArrayList<>();
for (List<DatabaseRecord> databaseRecords : databaseRecordsLists) {
mostRecentRecords.add(databaseRecords.stream()
.max(Comparator.comparing(DatabaseRecord::getTimestamp))
.orElseThrow(NoSuchElementException::new));
}
I've looked into the flatMap api, but then I'll only end up with a single map of all DatabaseRecord objects, where I need a max from each individual list.
Any ideas on a cleaner way to accomplish this?

You don't need flatMap. Create a Stream<List<DatabaseRecord>>, and map each List<DatabaseRecord> of the Stream to the max element. Then collect all the max elements into the output List.
List<DatabaseRecord> mostRecentRecords =
databaseRecordsLists.stream()
.map(list -> list.stream()
.max(Comparator.comparing(DatabaseRecord::getTimestamp))
.orElseThrow(NoSuchElementException::new))
.collect(Collectors.toList());

Based on the comments, I suggested to rather ignore the empty collection, otherwise, no result would be returned and NoSuchElementException thrown even the empty collection might (?) be a valid state. If so, you can improve the current solution:
databaseRecordsLists.stream()
.filter(list -> !list.isEmpty()) // Only non-empty ones
.map(list -> list.stream()
.max(Comparator.comparing(DatabaseRecord::getTimestamp)) // Get these with max
.orElseThrow(NoSuchElementException::new)) // Never happens
.collect(Collectors.toList()); // To List
If you use a version higher than Java 8:
As of Java 10, orElseThrow(NoSuchElementException::new) can be subsituted with orElseThrow().
As of Java 11, you can use Predicate.not(..), therefore the filter part would look like: .filter(Predicate.not(List::isEmpty)).

Related

java 8 double findFirst on stream

I have a HashMap that contains a single value which is an ArrayList that also contains a single value as well. I need to extract the single value from the ArrayList. At the moment I'm doing it like this:
map.values()
.stream()
.findFirst()
.orElse(new ArrayList<>()).stream().findFirst().orElse(null)
This works, but I hope for a more elegant way to get the first element of a stream inside the first element of a stream.
In other words, I want eliminate the double stream().findFirst().
Is that possible?

Combine List of streams into one single stream

I have a List<Stream<String>> that I get by doing a series of transactions.
The list size is dynamic (Maximum 3 elements) so I can't do:
Stream<String> finalStream = Stream.concat(list.get(0),Stream.concat(list.get(1),list.get(2));
I need to concatenate the list of Streams into one single Stream<String>.
Is there any simple way to do this?
If you have a list of lists, or a stream of streams, or any collection of collections, you can use flatMap to, well, flatten them. flatMap applies a mapping function which must return a stream to an input and streams each element of the result of the mapping function.
In your case, you could do:
var finalStream = list.stream().flatMap(x -> x);
x -> x is the identify function which returns the input unmodified. If you prefer, you can replace it with the expression Function.identity().

How to convert List<Optional<Type>> into List<Type>

I have extracted values from a Map into a List but got a List<Optional<TXN_PMTxnHistory_rb>>, and I want to convert it into List<TXN_PMTxnHistory_rb>.
My code:
List<Optional<TXN_PMTxnHistory_rb>> listHistory_rb6 =
listHistory_rb5.values()
.stream()
.collect(Collectors.toList());
I'd like to obtain a List<TXN_PMTxnHistory_rb>.
Filter out all the empty values and use map to obtain the non-empty values:
List<TXN_PMTxnHistory_rb> listHistory_rb6 =
listHistory_rb5.values()
.stream()
.filter(Optional::isPresent)
.map(Optional::get)
.collect(Collectors.toList());
It's possible to do this using a method called flatMap on the stream of Optionals which will remove any 'empty' Optionals.
List<TXN_PMTxnHistory_rb> listHistory_rb6 =
listHistory_rb5.values()
.stream()
.flatMap(Optional::stream)
.collect(Collectors.toList());
Flatmap is essentially performing two things - "mapping" and "flattening". In the mapping phase it calls whatever method you've passed in and expects back a new stream - in this case each Optional in your original List will become a Stream containing either 1 or 0 values.
The flatten phase will then create a new Stream containing the results of all the mapped Streams. Thus, if you had 2 Optional items in your List, one empty and one full, the resulting Stream would contain 0 elements from the first mapped Stream, and 1 value from the second.
Another option is to get all values and then filter out nulls:
List<TXN_PMTxnHistory_rb> listHistory_rb6 =
listHistory_rb5.values().stream()
.map(opt -> opt.orElse(null))
.filter(Objects::nonNull)
.collect(Collectors.toList());

Java Streams: Organize a collection into a map and select smallest key

I'm pretty sure this is not possible in one line, but I just wanted to check:
List<WidgetItem> selectedItems = null;
Map<Integer, List<WidgetItem>> itemsByStockAvailable = WidgetItems.stream()
.collect(Collectors.groupingBy(WidgetItem::getAvailableStock));
selectedItems = itemsByStockAvailable.get(
itemsByStockAvailable.keySet().stream().sorted().findFirst().get());
Basically I'm collecting all widget items into a map where the key is the availableStock quantity and the value is a list of all widgets that have that quantity (since multiple widgets might have the same value). Once I have that map, I would want to select the map's value that corresponds to the smallest key. The intermediate step of creating a Map isn't necessary, it's just the only way I could think of to do this.
It appears what you want is to keep all the widget items that were grouped with the lowest available stock. In that case, you can collect the grouped data into a TreeMap to ensure the ordering based on increasing values of the stock and retrieve the first entry with firstEntry()
List<WidgetItem> selectedItems =
widgetItems.stream()
.collect(Collectors.groupingBy(
WidgetItem::getAvailableStock,
TreeMap::new,
Collectors.toList()
))
.firstEntry()
.getValue();
The advantage is that it is done is one-pass over the initial list.
Essentially you want to get all the input elements which are minimal according to the custom comparator Comparator.comparingInt(WidgetItem::getAvailableStock). In general this problem could be solved without necessity to store everything into the intermediate map creating unnecessary garbage. Also it could be solved in single pass. Some interesting solutions already present in this question. For example, you may use the collector implemented by Stuart Marks:
List<WidgetItem> selectedItems = widgetItems.stream()
.collect(maxList(
Comparator.comparingInt(WidgetItem::getAvailableStock).reversed()));
Such collectors are readily available in my StreamEx library. The best suitable in your case is MoreCollectors.minAll(Comparator):
List<WidgetItem> selectedItems = widgetItems.stream()
.collect(MoreCollectors.minAll(
Comparator.comparingInt(WidgetItem::getAvailableStock)));
If you want to avoid creating the intermediate map, you can first determine the smallest stock value, filter by that value and collect to list.
int minStock = widgetItems.stream()
.mapToInt(WidgetItem::getAvailableStock)
.min()
.getAsInt(); // or throw if list is empty
List<WidgetItem> selectItems = widgetItems.stream()
.filter(w -> minStock == w.getAvailableStock())
.collect(toList());
Also, do not use sorted().findFirst() to find the min value of a stream. Use min instead.
You can find the smallest key in a first pass and then get all the items having that smallest key:
widgetItems.stream()
.map(WidgetItem::getAvailableStock)
.min(Comparator.naturalOrder())
.map(min ->
widgetItems.stream()
.filter(item -> item.getAvailableStock().equals(min))
.collect(toList()))
.orElse(Collections.emptyList());
I would collect the data into a NavigableMap, which involves only a small change to your original code:
List<WidgetItem> selectedItems = null;
NavigableMap<Integer, List<WidgetItem>> itemsByStockAvailable =
WidgetItems.stream()
.collect(Collectors.groupingBy(WidgetItem::getAvailableStock,
TreeMap::new, Collectors.toList()));
selectedItems = itemsByStockAvailable.firstEntry().getValue();

Find most common/frequent element in an ArrayList in Java

I have an array list with 5 elements each of which is an Enum. I want to build a method which returns another array list with the most common element(s) in the list.
Example 1:
[Activities.WALKING, Activities.WALKING, Activities.WALKING, Activities.JOGGING, Activities.STANDING]
Method would return: [Activities.WALKING]
Example 2:
[Activities.WALKING, Activities.WALKING, Activities.JOGGING, Activities.JOGGING, Activities.STANDING]
Method would return: [Activities.WALKING, Activities.JOGGING]
WHAT HAVE I TRIED:
My idea was to declare a count for every activity but that means that if I want to add another activity, I have to go and modify the code to add another count for that activity.
Another idea was to declare a HashMap<Activities, Integer> and iterate the array to insert each activity and its occurence in it. But then how will I extract the Activities with the most occurences?
Can you help me out guys?
The most common way of implementing something like this is counting with a Map: define a Map<MyEnum,Integer> which stores zeros for each element of your enumeration. Then walk through your list, and increment the counter for each element that you find in the list. At the same time, maintain the current max count. Finally, walk through the counter map entries, and add to the output list the keys of all entries the counts of which matches the value of max.
In statistics, this is called the "mode" (in your specific case, "multi mode" is also used, as you want all values that appear most often, not just one). A vanilla Java 8 solution looks like this:
Map<Activities, Long> counts =
Stream.of(WALKING, WALKING, JOGGING, JOGGING, STANDING)
.collect(Collectors.groupingBy(s -> s, Collectors.counting()));
long max = Collections.max(counts.values());
List<Activities> result = counts
.entrySet()
.stream()
.filter(e -> e.getValue().longValue() == max)
.map(Entry::getKey)
.collect(Collectors.toList());
Which yields:
[WALKING, JOGGING]
jOOλ is a library that supports modeAll() on streams. The following program:
System.out.println(
Seq.of(WALKING, WALKING, JOGGING, JOGGING, STANDING)
.modeAll()
.toList()
);
Yields:
[WALKING, JOGGING]
(disclaimer: I work for the company behind jOOλ)

Categories

Resources