How to check if an object of one list contains any object of another list

Viewed 172

We have two lists one is of type Question and the other of type Tag.

The Question class has those attributes

private String id;
private String header;
private String content;
private List<Tag> tags;
private Long timeStamp;

The Question list has all questions in it and the Tag list has all tags in it. We wanna check if One question contains any tag of the tag list. I want to do this for all questions.

With question.getTags, I get the list of tags.

The Tag class has an attribute called counter.

I will give some pseudocode to actually show you what I wanna do

List<Tag> allTags = ...
List<Question> allQuestions = ...
Map<Tag,Integer> map = new Hashmap<>();

if(one question contains any tag of allTags) {
     tag.setCounter(counter+1);
     map.put(tag,tag.getCounter);
}

In the end I wanna have a map where the key is the tag and the value the counter of that tag.

How can I actually do this?

EDIT here is my Tag.java

@NoArgsConstructor
@Data
@Getter
@AllArgsConstructor
@Setter
@Document(collection = "tag")
public class Tag {
    private String id;
    private String name;
    private Long timeStamp;

public Tag(String name) {
        this.id = UUID.randomUUID().toString();
        this.name = name;
        this.timeStamp = Instant.now().getEpochSecond()*1000;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Tag tag = (Tag) o;
        return Objects.equals(getId(), tag.getId());
    }

    @Override
    public int hashCode() {
        return Objects.hash(getId());
    }
}
2 Answers

I think a counter attribute in the Tag class to count how often a Tag occurs in a list of questions is a design error. You don't need such a counter there.

A possibly plausible example: Imagine you have a class Student and a class Course. To keep track of how many students are in a course, there is no need for a counter in the Student class. In the same way a class Tag should only contain the attributes of a tag. That said, you can achieve your goal without the counter with either one of the two following approachs (provided you have java 8 or higher and your Tag class overrides the equals and hashcode methods):

Approach 1 using Streams & Collectors.groupingBy

Map<Tag,Long> map = allQuestions.stream()
            .flatMap(q -> q.getTags().stream())
            .filter(allTags::contains)
            .collect(Collectors.groupingBy(Function.identity(),Collectors.counting()));

Approach 2 using List.forEach & Map.compute

Map<Tag, Integer> map2 = new HashMap<>();
    allQuestions.forEach(q -> {
        q.getTags()
                .stream()
                .filter(allTags::contains)
                .forEach(t -> map2.compute(t, (k, v) -> v == null ? 1 : v + 1));
    });

Or using a traditional for-loop and if-else block

Map<Tag, Integer> map3 = new HashMap<>();

for (Question q : allQuestions) {
    for (Tag t : q.getTags()) {
        if (allTags.contains(t)) {
            if (map3.containsKey(t)) {
                map3.put(t, map3.get(t) + 1);
            } else {
                map3.put(t, 1);
            }
        }
    }
}

I think this will work perfectly

    int count=0; 
    for(Question q : allQuestions){
     for(Tag t : q.getTags()){
     if(allTags.contains(t)){ 
        count=map.containsKey(t) ? map.get(t)+1 : 0;
        map.put(t,count);
       }
     }
    }
Related