callmedope
callmedope

Reputation: 83

Streams, check that two lists of objects has same nested lists of another objects

I have List of log events that I shoud check, that they are the same as should be. There is structure of Event object:

public class Event {
    public String type;
    public List<Item> items;

    public Event(String type, List<Item> items) {
        this.type = type;
        this.items = items;
    }
}
public class Item {
    public String id;
    public String value;

    public Item(String id, String value) {
        this.id = id;
        this.value = value;
    }
}

Lets fill "should" object, "real" object and check that they have same items

List<Event> should = asList(
                new Event("1000", asList(new Item("server_id", "1"), new Item("user_id", "11"))),
                new Event("1000", asList(new Item("server_id", "1"), new Item("user_id", "11"))));
        List<Event> logged = asList(
                new Event("1000", asList(new Item("server_id", "1"), new Item("user_id", "11"))),
                new Event("1000", asList(new Item("server_id", "1"), new Item("user_id", "11"))));
        boolean logMatch = logged.stream()
                                    .allMatch(e1 ->
                                            should.stream()
                                                      .allMatch(e2 -> e2.items
                                                                        .stream()
                                                                        .allMatch(a2 -> e1.items
                                                                                          .stream()
                                                                                          .anyMatch(a1 -> a1.value.equals(a2.value)))));
        System.out.println(logMatch);

It's true, but I have an issue, if I change any value of "should" to "11", I'll get true. How can I fix this or how to make this comparison simpler?

Upvotes: 1

Views: 1292

Answers (1)

ernest_k
ernest_k

Reputation: 45309

Your problem is that you're just checking that each logged element has at least an element in the should list with which it has the same value. You rather want to check that the two value collections are equivalent.

A simple correction can use list comparison in this case:

List<String> actualValues =  logged.stream().flatMap(l -> l.items.stream())
        .map(i -> i.value).collect(Collectors.toList());
List<String> shouldValues = should.stream().flatMap(l -> l.items.stream())
        .map(i -> i.value).collect(Collectors.toList());

boolean logMatch = actualValues.equals(shouldValues);

This comparison is strict and takes order into account. If you'd rather ignore the order of elements, you can collect to set to ignore the order of items.


EDIT: You can compare actual events if you need to check multiple fields. The following uses a predicate:

List<Item> actualValues =  logged.stream().flatMap(l -> l.items.stream())
        .collect(Collectors.toList());
List<Item> shouldValues = should.stream().flatMap(l -> l.items.stream())
        .collect(Collectors.toList());

BiPredicate<Item, Item> predicate = (item1, item2) -> item1.id.equals(item2.id) 
        && item1.value.equals(item2.value);

boolean res = actualValues.size() == shouldValues.size() //should be same length
        && IntStream.range(0, actualValues.size())
        .filter(i -> !predicate.test(actualValues.get(i), shouldValues.get(i)))
        .count() == 0; //all elements's ID and value must match

Upvotes: 3

Related Questions