Using Java 8 Streams' Collectors to increment value based of existing key/value pair

Suppose there is a List<Object> and that Object contains two methods: getUserId and getPoints.

Consider that List<Object> contains three objects, and they contain the following data:

userId A; 3 points
userId A; 5 points
userId B; 1 point

After collecting this properly, I am expecting to have a Map<String, Integer> that would look like this:

A: 8,
B: 1

I am attempting this using Java’s 8 functional interfaces, the streams, because I only need the final result and I do not have nor need any of these mappings in between. I am willing to modify this if there is no other way.
I first came up with this:

this.service.getListObject()
    .stream()
    .collect(Collectors.toMap(Object::getUserId, Object::getPoints));

However, this has shown insufficient because it will always replace the key with the newest value, thus producing:

A: 5,
B: 1

How can I tweak the last bits from the Collectors.toMap() to automatically increment its value according to the value already stored in the map, so that I produce the result above?

Solution:

Use Collectors.groupingBy together with Collectors.summingInt as a downstream collector:

this.service.getListObject()
    .stream().collect(Collectors.groupingBy(
        Object::getUserId, Collectors.summingInt(Object::getPoints)));

Is there any equivalent Kotlin function to Java 8 Stream limit function

I am trying to find the first two elements in a list that meet a condition (filtering), for that purpose I have implemented the following piece of code in kotlin:

val arr = 0 until 20

val res = arr.filter { i ->
        println("Filter: $i")
        i % 2 == 0
    }.take(2)

Everything was fine until I realized it filters through the whole list, no matter if the two elements have been found.

Making use of Java 8 stream api, it works as expected.

val res2 = arr.toList().stream()
     .filter { i ->
          println("Filter: $i")
          i % 2 == 0
     }.limit(2)

So my questions is if it can be achieved using only Kotlin functions.

I know I could use a simple for loop but I want to use a functional programming aproach.

Solution:

Kotlin, by default, does these kind of operations eagerly whereas Streams in Java are lazy. You can have the same in Kotlin if you work with sequences, which can easily be generated from Arrays or Iterables with asSequence().

arr.asSequence().filter { i ->
    println("Filter: $i")
    i % 2 == 0
}.take(2)

You can read about the details here.

Combine two Functions in Java8

In isReadyToDeliver method if all products in order is available (ProductState.AVAILABLE) and if order state is ready to send (OrderState.READY_TO_SEND), method must return true.
I wrote both two part but I couldn’t combine them in return phrase,

I wrote return orderState.andThen(productState) but get this error:

The method andThen(Function<? super Boolean,? extends V>) in the type Function<Order,Boolean> is not applicable for the arguments (Function<Order,Boolean>)

public class OrderFunctions  {

    public Function<Order, Boolean> isReadyToDeliver() {            
        Function<Order, Boolean> orderState = o -> o.getState() == OrderState.READY_TO_SEND;            
        Function<Order, Boolean>  productState = 
                o -> o.getProducts()
                    .stream()
                    .map(Product -> Product.getState())
                    .allMatch(Product -> Product == ProductState.AVAILABLE);

        return ????? ; 
       //return  orderState.andThen(productState);
       //error: The method andThen(Function<? super Boolean,? extends V>) in the type Function<Order,Boolean> is not applicable for the arguments (Function<Order,Boolean>)      
    }
}

In case other classes are needed:

enum OrderState {CONFIRMED, PAID, WAREHOUSE_PROCESSED, READY_TO_SEND, DELIVERED }

enum ProductType { NORMAL, BREAKABLE, PERISHABLE }

public class Order {

    private OrderState state;
    private List<Product> products = new ArrayList<>();

    public OrderState getState() {
        return state;
    }

    public void setState(OrderState state) {
        this.state = state;
    }

    public Order state(OrderState state) {
        this.state = state;
        return this;
    }

    public List<Product> getProducts() {
        return products;
    }

    public void setProducts(List<Product> products) {
        this.products = products;
    }

    public Order product(Product product) {
        if (products == null) {
            products = new ArrayList<>();
        }
        products.add(product);
        return this;
    }
}

public class Product {

    private String code;
    private String title;
    private ProductState state;

    public ProductState getState() {
        return state;
    }

    public void setState(ProductState state) {
        this.state = state;
    }

    public Product state(ProductState state) {
        this.state = state;
        return this;
    }
}

Solution:

If you change isReadyToDeliver() to return Predicate<Order> then you will be able to combine two predicates with .and(Predicate another) function:

public Predicate<Order> isReadyToDeliver() {
    Predicate<Order> orderState = o -> o.getState() == OrderState.READY_TO_SEND;

    Predicate<Order> productState =
                o -> o.getProducts()
                   .stream()
                   .map(Product -> Product.getState())
                   .allMatch(Product -> Product == ProductState.AVAILABLE);

    return orderState.and(productState);
}

Your example with functions composition didn’t work, because when you compose functions f and g, g takes as a parameter value that f function returns. In your case it was broken, because orderState expected Order and return Boolean and it this case orderState.andThen() expected a function that takes Boolean as a parameter and returns something else. This requirement was not satisfied, because productState expected Order and returned Boolean. This is exactly what following error said:

error: The method andThen(Function) in the type Function is not applicable for the arguments (Function)

But if for some reason you want to stay with Function<Order, Boolean> then you will have return a lambda like:

public Function<Order, Boolean> isReadyToDeliver() {
    Function<Order, Boolean> orderState = o -> o.getState() == OrderState.READY_TO_SEND;

    Function<Order, Boolean> productState =
            o -> o.getProducts()
                    .stream()
                    .map(Product -> Product.getState())
                    .allMatch(Product -> Product == ProductState.AVAILABLE);


    return (order) -> orderState.apply(order) && productState.apply(order);
}

How to return multiple values in Python map?

I have a list of dicts:

>>> adict = {'name': 'John Doe', 'age': 18}
>>> bdict = {'name': 'Jane Doe', 'age': 20}
>>> l = []
>>> l.append(adict)
>>> l.append(bdict)
>>> l
[{'age': 18, 'name': 'John Doe'}, {'age': 20, 'name': 'Jane Doe'}]

Now I want to split up the values of each dict per key. Currently, this is how I do that:

>>> for i in l:
...     name_vals.append(i['name'])
...     age_vals.append(i['age'])
...
>>> name_vals
['John Doe', 'Jane Doe']
>>> age_vals
[18, 20]

Is it possible to achieve this via map? So that I don’t have to call map multiple times, but just once?

name_vals, age_vals = map(lambda ....)

Solution:

A simple & flexible way to do this is to “transpose” your list of dicts into a dict of lists. IMHO, this is easier to work with than creating a bunch of separate lists.

lst = [{'age': 18, 'name': 'John Doe'}, {'age': 20, 'name': 'Jane Doe'}]
out = {}
for d in lst:
    for k, v in d.items():
        out.setdefault(k, []).append(v)

print(out)

output

{'age': [18, 20], 'name': ['John Doe', 'Jane Doe']}

But if you really want to use map and separate lists on this there are several options. Willem has shown one way. Here’s another.

from operator import itemgetter

lst = [{'age': 18, 'name': 'John Doe'}, {'age': 20, 'name': 'Jane Doe'}]
keys = 'age', 'name'
age_lst, name_lst = [list(map(itemgetter(k), lst)) for k in keys]
print(age_lst, name_lst)

output

[18, 20] ['John Doe', 'Jane Doe']

If you’re using Python 2, then the list wrapper around the map call isn’t necessary, but it’s a good idea to use it to make your code compatible with Python 3.

Replace lambda with method reference in flatMap during array mapping

Say we have a Customer class:

public class Customer {
    private Car[] cars;
    // getter, setter, constructor
}

and collection of customers which we need to map on cars.

Currently I’m doing it somehow like this:

Collection<Customer> customers = ...
customers.stream().flatMap(
        customer -> Arrays.stream(customer.getCars())
)...

It works well, but the code doesn’t look elegant. I’d really like to replace it with code that uses method references which usually looks more readable and more compact. But using a field of array type makes it hard.

Question: is there any way of enhancing the flatMap call so it will be more readable/compact/clear?

Solution:

You can split the flatMap call into two calls – map and flatMap – each receiving a method reference:

Collection<Customer> customers = ...
customers.stream()
         .map(Customer::getCars)
         .flatMap(Arrays::stream)...

Can I count my stream size if I limit it by a predicate dependent on the input?

I want to make a stream with random numbers. As soon as the numbers fullfill a certain condition I want to now how many iterations was needed.
So either I want to have the size of the stream or an Collection from which I can read then the size.

Here are my approaches:

random.ints(0, Integer.MAX_VALUE).anyMatch(a -> {return a < 20000;});

This gives me only the a boolean as soon as my condition is fullfilled.

random.ints(0, Integer.MAX_VALUE).filter(a -> a < 20000).limit(1).count();

And this returns obviously 1. But I want to have the size before I filtered my result. I further tried several things with a counting variable but since lambdas are capturing them effectifely final from outside I have an initialising problem.

Any help or hint is appreciated

Solution:

Java 9 has a feature to support that – takeWhile:

random.ints(0, Integer.MAX_VALUE).takeWhile(a -> a < 20000).count();