Related
I have a list of shop objects that are grouped by the item they have.
class Shop{
String shopName;
String item;
int size;
...}
How can I get a list of the 3 biggest shops (or n biggest shops) for each item?
ie. suppose I have
Shop("Walmart", "Hammer", 100);
Shop("Target", "Scissor", 30);
Shop("Walgreens", "Hammer", 300);
Shop("Glens", "Hammer", 500);
Shop("Walmart", "Scissor", 75);
Shop("Toms", "Hammer", 150);
I want to return a list of the top 3 shops grouped by item.
I grouped the items but i am not sure how to iterate through the given Map or entryset...
public class Shop {
int size;
String item;
String name;
public Shop(int size, String item, String name){
this.size = size;
this.item = item;
this.name = name;
}
//Return a list of the top 3 largest shops by item
public static void main(){
List<Shop> shops = new LinkedList<Shop>();
Comparator<Shop> shopComparator = new Comparator<Shop>(){
#Override
public int compare(Shop f1, Shop f2) {
return f1.getSize() < f2.getSize() ? 1 : -1;
}
};
shops.stream().collect(groupingBy(Shop::getItem))
.entrySet()
.stream()
.filter(entry -> entry.getValue().stream().map )
.forEach(item -> item.getValue())//Stuck here
;
}
}
The most important thing that you can learn about streams is that they aren't inherently "better" than equivalent approaches by any measure. Sometimes, they make code more readable, other times, less so. Use them to clarify your code, and avoid them when they obfuscate it.
This is a case where your code will be far more readable by using a collector for this purpose. Coding your own is fairly easy, and if you really want to understand streams better, I recommend it as a simple learning exercise.
Here, I'm using MoreCollectors.greatest() from the StreamEx library:
Comparator<Shop> bySize = Comparator.comparingInt(Shop::getSize);
Map<String, List<Shop>> biggestByItem
= shops.stream().collect(groupingBy(Shop::getItem, greatest(bySize, 3)));
This isn't better because it's shorter, or because it is faster and uses constant memory; it's better because complexity is factored out of the code, and hidden behind meaningful names that explain the behavior. Instead of littering your application with complex pipelines that need to be read, tested, and maintained independently, you have written (or referenced) a reusable collector with a clear behavior.
As I mentioned, there is a bit of a learning curve in understanding how the pieces of a Collector work together, but it's worth studying. Here's a possible implementation for a similar collector:
public static <T> Collector<T, ?, List<T>> top(int limit, Comparator<? super T> order) {
if (limit < 1) throw new IndexOutOfBoundsException(limit);
Objects.requireNonNull(order);
Supplier<Queue<T>> supplier = () -> new PriorityQueue<>(order);
BiConsumer<Queue<T>, T> accumulator = (q, e) -> collect(order, limit, q, e);
BinaryOperator<Queue<T>> combiner = (q1, q2) -> {
q2.forEach(e -> collect(order, limit, q1, e));
return q1;
};
Function<Queue<T>, List<T>> finisher = q -> {
List<T> list = new ArrayList<>(q);
Collections.reverse(list);
return list;
};
return Collector.of(supplier, accumulator, combiner, finisher, Collector.Characteristics.UNORDERED);
}
private static <T> void collect(Comparator<? super T> order, int limit, Queue<T> q, T e) {
if (q.size() < limit) {
q.add(e);
} else if (order.compare(e, q.peek()) > 0) {
q.remove();
q.add(e);
}
}
Given this factory, it's trivial to create others that give you bottom(3, bySize), etc.
You may be interested in this related question and its answers.
Well, you could take the following steps:
With groupingBy(Shop::getItem), you could create a map which sorts by the item, so your result would be a Map<String, List<Shop>>, where the list contains all shops with that item.
Now we need to sort the List<Shop> in reversed order, so the top items of the list are the shops with the largest size. In order to do this, we could use collectingAndThen as downstream collector to groupingBy.
Collectors.collectingAndThen(Collectors.toList(), finisherFunction);
Our finisher function should sort the list:
list -> {
Collections.sort(list, Comparator.comparing(Shop::size).reversed());
return list;
}
This would result in a Map<String, List<Shop>>, where the list is sorted, highest size first.
Now the only thing we need to do, is limiting the list size to 3. We could use subList. I think subList throws an exception if the list contains less than 3 items, so we need to use Math.min(3, list.size()) to take this into account.
list -> {
Collections.sort(list, Comparator.comparing(Shop::size).reversed());
return list.subList(0, Math.min(3, list.size()));
}
The whole code then looks like this:
shops.stream()
.collect(groupingBy(Shop::item, Collectors.collectingAndThen(Collectors.toList(), list -> {
Collections.sort(list, Comparator.comparing(Shop::size).reversed());
return list.subList(0, Math.min(3, list.size()));
})));
Online demo
Instead of 'manually' sorting the list and limiting it to 3, you could create a small class which automatically does this — both limit and sort the list upon adding elements.
Not as fancy as MC Emperor but it seems to work.
I started from the part you already did:
shops.stream().collect(Collectors.groupingBy(Shop::getItem))
.entrySet().stream().map(entry -> {
entry.setValue(entry.getValue().stream()
.sorted(Comparator.comparingInt(s->-s.size))
.limit(3) // only keep top 3
.collect(Collectors.toList()));
return entry;
}).forEach(item -> {
System.out.println(item.getKey()+":"+item.getValue());
});
You can use groupingBy along with limit to get desired result:
import static java.util.stream.Collectors.*;
// Define the sort logic. reversed() applies asc order (Default is desc)
Comparator<Shop> sortBySize = Comparator.comparingInt(Shop::getSize).reversed();
int limit = 3; // top n items
var itemToTopNShopsMap = list.stream().collect(
collectingAndThen(groupingBy(Shop::getItem),
itemToShopsMap -> getTopNShops(sortBySize, itemToShopsMap, limit)));
static Map<String, List<Shop>> getTopNShops(Comparator<Shop> sortBy, Map<String, List<Shop>> inMap, int limit) {
var returningMap = new HashMap<String, List<Shop>>();
for (var i : inMap.entrySet()) {
returningMap.put(i.getKey(), i.getValue().stream().sorted(sortBy).limit(Long.valueOf(limit)).collect(toList()));
}
return returningMap;
}
We took following steps:
Group the List by 'item'
For each grouping, i.e., item to list of shops entry, we sort the list of shops by predefined sort logic and collect (limit) the top n results.
Note:
In static method getTopNShops, mutation of source map is avoided. We could have written this method as a stream, but the stream version may have been less readable than the foreach loop.
I need a method where I need to xor Predicates which I will recieve as method params. I have a somewhat working but cumbersome solution for two predicates. To give a simple, minimal and reproducible example:
Predicate<String> pred1 = s -> s.contains("foo");
Predicate<String> pred2 = s -> s.contains("bar");
String toTest = "foobar";
The logical OR will return true for given predicates and the test string:
boolean oneOnly = pred1.or(pred2).test(toTest);
but for my use case it should return false since both substrings are included. It should only return true if and only if one condition is met.
For two prdeicates I have this
static boolean xor(Predicate<String> pred1, Predicate<String> pred2, String toTest){
return pred1.and(pred2.negate()).or(pred2.and(pred1.negate())).test(toTest);
}
Is there a simple but a convinient way to xor predicates?
In followup to #xdhmoore's answer, that's overkill and can be done much simpler:
static <T> Predicate<T> xor(Predicate<T> pred1, Predicate<T> pred2) {
return t -> pred1.test(t) ^ pred2.test(t);
}
Update:
Below are some examples of why you'd want to return a Predicate instead of a boolean, but #rzwitserloot's answer does it nicely and more succinctly.
To play the Devil's advocate: it's less pretty, but one advantage for the way you already have it is you are slightly more in line with the Predicate idioms. A little tweaking gets you:
Return a Predicate
static <T> Predicate<T> xor(Predicate<T> pred1, Predicate<T> pred2){
return pred1.and(pred2.negate())
.or(pred2.and(pred1.negate()));
}
// Which means you can do this, which is probably more conducive to combining your
// new xor function with other predicates:
xor((Integer a) -> a > 1, (Integer b) -> b < 10).test(0));
// For example, because you return a Predicate:
xor((Integer a) -> a > 1, (Integer b) -> b < 10).negate().test(0));
Return a boolean
static <T> boolean xor(Predicate<T> pred1, Predicate<T> pred2, T toTest) {
return pred1.test(toTest) ^ pred2.test(toTest);
}
// In contrast, if your xor function returns a boolean, you get this, which is
// similar, but is less conducive to using all the Predicate methods:
xor((Integer a) -> a > 1, (Integer b) -> b < 10, 14);
// To be honest, this seems more readable to me than the negate() function in the
// example above, but perhaps there are scenarios where the above is preferred...
!xor((Integer a) -> a > 1, (Integer b) -> b < 10, 14)
Not a big deal, but your question made me curious...
You could reduce your xor'ed predicates to a single predicate with stream.reduce and then return the outcome.
like so:
import java.util.function.Predicate;
import java.util.Arrays;
public class MultiXor{
public static void main(String[] args){
System.out.println(xor("monkey", p -> p.equals("monkey"), p -> p.equals("dork"), p -> p.equalsIgnoreCase("Monkey")) );
System.out.println(true ^ false ^ true);
System.out.println(xor("monkey", p -> p.equals("monkey"), p -> p.equals("dork")) );
System.out.println(true ^ false);
}
public static <T> boolean xor(final T param, Predicate<T>... predicates){
return Arrays.stream(predicates).reduce( p -> false, (previous, p) -> r -> previous.test(param) ^ (p.test(param))).test(param);
}
}
I am practicing my java skills on Hyperskill and I cant figure out this excercise about composing predicates.
Write the disjunctAll method that accepts a list of IntPredicate's and returns a single IntPredicate. The result predicate is a disjunction of all input predicates.
If the input list is empty then the result predicate should return false for any integer value (always false).
Important. Pay attention to the provided method template. Do not change it.
public static IntPredicate disjunctAll(List<IntPredicate> predicates) {
}
A simple iteration of the list would do it:
public static IntPredicate disjunctAll(List<IntPredicate> predicates)
{
IntPredicate result = i -> false;
for (IntPredicate p: predicates) {
result = p.or(result);
}
return result;
}
or simply with a stream reducer:
public static IntPredicate disjunctAll(List<IntPredicate> predicates)
{
return predicates.stream()
.reduce(i -> false, IntPredicate::or);
}
Is it possible to check if an array (or collection) contains element 5 and element other than 5. In one stream returning boolean result instead of using two streams:
int[] ints = new int[]{1, 2, 3, 4, 5};
boolean hasFive = IntStream.of(ints).anyMatch(num -> num == 5);
boolean hasNonFive = IntStream.of(ints).anyMatch(num -> num != 5);
boolean result = hasFive && hasNonFive;
Here's two solutions involving my StreamEx library. The core feature I'm using here is the concept of short-circuiting collectors. My library enhances the Collector concept to provide the ability to short-circuit (which works both for sequential and parallel streams)
If predicates are like in your sample (one is the opposite of another), you may use partitioningBy:
Map<Boolean, Optional<Integer>> map = IntStreamEx.of(ints).boxed()
.partitioningBy(num -> num == 5, MoreCollectors.first());
Now you should check whether both mappings are present:
System.out.println(map.values().stream().allMatch(Optional::isPresent));
Or in single statement:
System.out.println(IntStreamEx.of(ints).boxed()
.partitioningBy(num -> num == 5, MoreCollectors.first())
.values().stream().allMatch(Optional::isPresent));
Here we're using MoreCollectors.first() short-circuiting collector. This solution is similar to one proposed by #user140547, but it will actually stop processing as soon as both elements are found.
For two custom predicates it's possible to use pairing collector which combines the results of two collectors (preserving the short-circuiting if input collectors are short-circuiting). But first, we need anyMatching collector (which is absent in my library):
import static one.util.streamex.MoreCollectors.*;
static <T> Collector<T, ?, Boolean> anyMatching(Predicate<T> pred) {
return collectingAndThen(filtering(pred, first()), Optional::isPresent);
}
Collector<Integer, ?, Boolean> hasFive = anyMatching(num -> num == 5);
Collector<Integer, ?, Boolean> hasNonFive = anyMatching(num -> num != 5);
Collector<Integer, ?, Boolean> hasBoth = pairing(hasFive, hasNonFive,
(res1, res2) -> res1 && res2);
System.out.println(IntStreamEx.of(ints).boxed().collect(hasBoth));
In this specific case, i.e. you want to know whether a stream or array contains both, a matching and a nonmatching element (an element matching the predicate’s negation), you can do it much simpler.
First, test whether the first element matches the predicate or its negation, then, search whether the stream contains any match of the opposite:
IntPredicate predicate=i -> i==5;
if(ints.length>0 && predicate.test(ints[0]))
predicate=predicate.negate();
boolean result = IntStream.of(ints).anyMatch(predicate);
That’s it. In case you don’t have an array or collection as the stream source, but an arbitrary stream, testing the first element is a bit trickier:
IntPredicate[] tmp={ null };
Spliterator.OfInt sp=intStream.spliterator();
boolean result = sp.tryAdvance(
(int i) -> tmp[0]=predicate.test(i)? predicate.negate(): predicate)
&& StreamSupport.intStream(sp, false).anyMatch(tmp[0]);
One way I see how to do that is to create a custom IntPredicate from multiple IntPredicates. Each time a value is tested, we try to find a predicate from this array that matches it and if it does, we store it inside an internal Set (to handle duplicates correctly). When the stored set has the same size as the initial array, it means all the predicates have been matched and our custom predicate can return true.
My initial solution used a Set<Integer> to store the indexes of the predicates that were matched. As #Holger commented, it may be more performant to use a BitSet and store the indexes of the unmatched predicates.
private static class MultipleIntPredicate implements IntPredicate {
private IntPredicate[] predicates;
private BitSet unmatchedPredicates;
public MultipleIntPredicate(IntPredicate... predicates) {
this.predicates = predicates;
unmatchedPredicates = new BitSet(predicates.length);
unmatchedPredicates.set(0, predicates.length, true); // initially, all predicates are unmatched
}
#Override
public boolean test(int value) {
unmatchedPredicates.stream()
.filter(i -> predicates[i].test(value))
.findFirst()
.ifPresent(unmatchedPredicates::clear); // when a match is found, clear the BitSet
return unmatchedPredicates.isEmpty(); // return true if all the predicates were matched
}
}
Using it like this:
int[] ints = new int[] {1, 2, 3, 4, 5};
MultipleIntPredicate predicate = new MultipleIntPredicate(num -> num == 5, num -> num != 5);
boolean hasFiveAndNonFive = IntStream.of(ints).anyMatch(predicate);
System.out.println(hasFiveAndNonFive);
For the case of an array, like in your question, this solution is probably more overhead than iterating over the array twice. However, in the case of an infinite IntStream, this predicate will still work correctly. It also has the advantage that the wanted predicates do not have to be opposite of themselves.
If you don't mind using a boxed stream and 2 predicates are enough, you can use Collectors.partitioningBy and just do something like:
Map<Boolean, List<Integer>> collect = IntStream.of(ints).boxed().collect(Collectors.partitioningBy(x -> x == 5));
boolean hasFive = !collect.get(true).isEmpty();
boolean hasNonFive = !collect.get(false).isEmpty();
Another solution (for multiple predicates) which is maybe not as performant as Tunaki's solution and probably creates too many arrays, but does not use a mutable BitSet...
Boolean[] result = IntStream.of(ints).mapToObj(i ->
new Boolean[]{four.test(i), five.test(i), six.test(i)}
).reduce(new Boolean[]{false, false, false}, Test::or);
I do have a simialar problem like descripted here. But with two differences first I do use the stream api and second I do have an equals() and hashCode() method already. But within the stream the equalitity of the of Blogs are in this context not the same as defined in the Blog class.
Collection<Blog> elements = x.stream()
... // a lot of filter and map stuff
.peek(p -> sysout(p)) // a stream of Blog
.? // how to remove duplicates - .distinct() doesn't work
I do have a class with an equal Method lets call it ContextBlogEqual with the method
public boolean equal(Blog a, Blog b);
Is there any way removing all duplicate entries with my current stream approach based on the ContextBlogEqual#equal method?
I thought already on grouping, but this doesn't work either, because the reason why blogA and blogB is equal isn't just one parameter. Also I have no idea how I could use .reduce(..), because there is useally more than one element left.
In essence, you either have to define hashCode to make your data work with a hashtable, or a total order to make it work with a binary search tree.
For hashtables you'll need to declare a wrapper class which will override equals and hashCode.
For binary trees you can define a Comparator<Blog> which respects your equality definition and adds an arbitrary, but consistent, ordering criterion. Then you can collect into a new TreeSet<Blog>(yourComparator).
First, please note that equal(Blog, Blog) method is not enough for the most scenarios as you will need to pairwise compare all the entries which is not efficient. It's better to define the function which extracts new key from the blog entry. For example, let's consider the following Blog class:
static class Blog {
final String name;
final int id;
final long time;
public Blog(String name, int id, long time) {
this.name = name;
this.id = id;
this.time = time;
}
#Override
public int hashCode() {
return Objects.hash(name, id, time);
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null || getClass() != obj.getClass())
return false;
Blog other = (Blog) obj;
return id == other.id && time == other.time && Objects.equals(name, other.name);
}
public String toString() {
return name+":"+id+":"+time;
}
}
Let's have some test data:
List<Blog> blogs = Arrays.asList(new Blog("foo", 1, 1234),
new Blog("bar", 2, 1345), new Blog("foo", 1, 1345),
new Blog("bar", 2, 1345));
List<Blog> distinctBlogs = blogs.stream().distinct().collect(Collectors.toList());
System.out.println(distinctBlogs);
Here distinctBlogs contains three entries: [foo:1:1234, bar:2:1345, foo:1:1345]. Suppose that it's undesired, because we don't want to compare the time field. The simplest way to create new key is to use Arrays.asList:
Function<Blog, Object> keyExtractor = b -> Arrays.asList(b.name, b.id);
The resulting keys already have proper equals and hashCode implementations.
Now if you fine with terminal operation, you may create a custom collector like this:
List<Blog> distinctByNameId = blogs.stream().collect(
Collectors.collectingAndThen(Collectors.toMap(
keyExtractor, Function.identity(),
(a, b) -> a, LinkedHashMap::new),
map -> new ArrayList<>(map.values())));
System.out.println(distinctByNameId);
Here we use keyExtractor to generate the keys and merge function is (a, b) -> a which means select the previously added entry when repeating key appears. We use LinkedHashMap to preserve the order (omit this parameter if you don't care about order). Finally we dump the map values into the new ArrayList. You can move such collector creation to the separate method and generalize it:
public static <T> Collector<T, ?, List<T>> distinctBy(
Function<? super T, ?> keyExtractor) {
return Collectors.collectingAndThen(
Collectors.toMap(keyExtractor, Function.identity(), (a, b) -> a, LinkedHashMap::new),
map -> new ArrayList<>(map.values()));
}
This way the usage will be simpler:
List<Blog> distinctByNameId = blogs.stream()
.collect(distinctBy(b -> Arrays.asList(b.name, b.id)));
Essentially, you'll need a helper method like this one:
static <T, U> Stream<T> distinct(
Stream<T> stream,
Function<? super T, ? extends U> keyExtractor
) {
final Map<U, String> seen = new ConcurrentHashMap<>();
return stream.filter(t -> seen.put(keyExtractor.apply(t), "") == null);
}
It takes a Stream, and returns a new Stream that contains only distinct values given the keyExtractor. An example:
class O {
final int i;
O(int i) {
this.i = i;
}
#Override
public String toString() {
return "O(" + i + ")";
}
}
distinct(Stream.of(new O(1), new O(1), new O(2)), o -> o.i)
.forEach(System.out::println);
This yields
O(1)
O(2)
Disclaimer
As commented by Tagir Valeev here and in this similar answer by Stuart Marks, this approach has flaws. The operation as implemented here...
is unstable for ordered parallel streams
is not optimal for sequential streams
violates the stateless predicate constraint on Stream.filter()
Wrapping the above in your own library
You can of course extend Stream with your own functionality and implement this new distinct() function in there, e.g. like jOOλ or Javaslang do:
Seq.of(new O(1), new O(1), new O(2))
.distinct(o -> o.i)
.forEach(System.out::println);