'How to Collect elements of the Stream with incosistent data into a Map?

I have a method that receives a List of emails and ids as Strings.

I am making an API call then which has a limit of 100 email searches per call:

userService.listUsersByEmails(List<String> emails)

It returns a list of User objects.

Currently, I am collecting the results into a single List of User objects.

 List<User> users=  Lists.partition(emails, 100)//
    .stream()
    .map(userService:: listUsersByEmails)// this method makes API calls which takes 100 
      .flatMap(List::stream)             // emails as params (comma separated) & returns 
      .collect(Collectors.toList());     // User objects for each email found

The listUserByEmails method returns User object for each email found in the DB.

But a few results might not have the emailAddress attribute, but they are guaranteed to have an Id, firstName and LastName.

I am hoping to create a map of type Map<String, User>s like that

Key (emailAddress) - "[email protected]" , Value - John Doe's User object. 

The User objects that gets returned looks like this

 {id: 123456789
    emailAddress: [email protected]
    firstName: John
    lastName: Doe
    displayName: Sharon
    title: null
    company:  DEVELOP
    username: s-dev
    location: null
    accountType: NORMAL}

Is there any option to handle all Users (with email and without) together in one go?



Solution 1:[1]

This approach will allow to handle all User objects in the stream regardless of whether they have an email.

Collectors.partitioningBy splits the data into two parts based on the given predicate (whether email is present or not), and returns map with a key of type Boolean.

Then, based on this map, the two district maps userByEmail and userById are being populated.

That's how both maps could be encapsulated in a class and accessed via an instance of this class:

public class UserStorage {
    private Map<String, User> userByEmail = new HashMap<>();
    private Map<Long, User> userById = new HashMap<>();

    public void addUsers(List<User> source) {
        Map<Boolean, User> userByHasEmail = source
                .stream()
                .map(userService:: listUsersByEmails)
                .flatMap(List::stream)
                .collect(Collectors.partitioningBy(
                        user -> user.getEmailAddress() != null && !user.getEmailAddress().isBlank(),
                        Function.identity()));

        for (Map.Entry<Boolean, User> entry: userByHasEmail.entrySet()) {
            if (entry.getKey()) {
                userByEmail.put(entry.getValue().getEmailAddress(), entry.getValue());
            } else {
                userById.put(entry.getValue().getId(), entry.getValue());
            }
        }
    }

    public User getUser(String email) {
        return userByEmail.get(email);
    }

    public User getUser(Long id) {
        return userById.get(id);
    }

    public User getUser(String email, Long id) {
        return userByEmail.getOrDefault(email, userById.get(id));
    }
}
public static void main(String[] args) {
    UserStorage storage = new UserStorage();
    storage.addUsers(Lists.partition(emails, 100));

    User userWithEmail = storage.getUser("[email protected]");
    User userWithNoEmail = storage.getUser(123456789L);
    User user = storage.getUser("anyString", 123456789L);
}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1