0

I have a list of objects, for example

    List<Document> myList = new ArrayList<Document>();
    myList.add(new Document("TypeA"));
    myList.add(new Document("TypeB"));
    myList.add(new Document("TypeA"));
    myList.add(new Document("TypeA"));
    myList.add(new Document("TypeB"));

Now every Document has a different type property, which is what I pass to the constructor (It's not the real case, just trying to simplify it.

What I need to do is, add a new property "counter" to all of the Document objects, with the same Type. So the three Document objects with "TypeA" need to have incrementing counter values (1,2,3). If there is only one Document with a specific type, then it shouldn't have a counter value.

So I guess I basically need to somehow filter (group) them and then iterate them. But how can I do this efficiently?

I have JavaScript background, and there it would be easy for me, but with Java all I found, seems to be complicated.

Petr Aleksandrov
  • 1,434
  • 9
  • 24
user5417542
  • 3,146
  • 6
  • 29
  • 50
  • https://stackoverflow.com/questions/21678430/group-a-list-of-objects-by-an-attribute-java – JB Nizet Dec 03 '19 at 12:14
  • One of the ways it could be done is that you could maintain a map object with type as the key and running counter as the value. Then lookup this map and appropriately assign the counter property and update the map when new Dicument of existing type gets added to the list. – S B Dec 03 '19 at 12:15

3 Answers3

1

You can use data structure that store count, for example Bag

Bag is a collection that allows storing multiple items along with their repetition count:

    Bag<Integer> bag = new HashBag<>(
    Arrays.asList(1, 2, 3, 3, 3, 1, 4));             
    assertThat(2, equalTo(bag.getCount(1)));
Ori Marko
  • 56,308
  • 23
  • 131
  • 233
1

You can use java stream api to count Document types with grouping:

        final Map<String, Long> collect = myList.stream()
                .collect(groupingBy(Document::getType, counting()));
        System.out.println(collect);

Another possible option is adding static counter to the Document class:

class Document {

    private static Map<String, Integer> counter = new HashMap<>();

    public Document(String type) {
        counter.compute(type, (k, v) -> (v == null) ? 1 : v  + 1); // setting counter = 1 for new type, increasing for existing
    }
...
}
Petr Aleksandrov
  • 1,434
  • 9
  • 24
0

Having a field counter inside each Document sounds like a terrible design because you would have to update all references on every new object added or worst, have static fields updated. You should favor immutable designs as a good practice instead.

Here are the methods that would do the job.

Map<String, Long> documentCount = myList.stream().collect(groupingBy(Document::getType, counting()));
Map<String, Long> filteredDocumentCount = documentCount.entrySet().stream().filter(x -> x.getValue() > 1).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));

or perhaps the more expressive version:

Map<String, Long> documentCount = myList.stream().collect(groupingBy(Document::getType, counting()));
Predicate<Map.Entry<String,Long>> moreThanOneOccurrence = x -> x.getValue() > 1;
Map<String, Long> filteredDocumentCount = documentCount.entrySet().stream().filter(moreThanOneOccurrence).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
Tomas Fornara
  • 1,280
  • 8
  • 12