Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a scala/java equivalent of Python 3's collections.Counter

Tags:

java

python

scala

I want a class that will count the the number of objects I have - that sounds more efficient that gathering all the objects and then grouping them.

Python has an ideal structure in collections.Counter, does Java or Scala have a similar type?

like image 525
Salim Fadhley Avatar asked Jan 31 '15 17:01

Salim Fadhley


3 Answers

From the documentation that you linked:

The Counter class is similar to bags or multisets in other languages.

Java does not have a Multiset class, or an analogue. Guava has a MultiSet collection, that does exactly what you want.

In pure Java, you can use a Map<T, Integer> and the new merge method:

final Map<String, Integer> counts = new HashMap<>();

counts.merge("Test", 1, Integer::sum);
counts.merge("Test", 1, Integer::sum);
counts.merge("Other", 1, Integer::sum);
counts.merge("Other", 1, Integer::sum);
counts.merge("Other", 1, Integer::sum);

System.out.println(counts.getOrDefault("Test", 0));
System.out.println(counts.getOrDefault("Other", 0));
System.out.println(counts.getOrDefault("Another", 0));

Output:

2
3
0

You can wrap this behaviour in a class in a few lines of code:

public class Counter<T> {
    final Map<T, Integer> counts = new HashMap<>();

    public void add(T t) {
        counts.merge(t, 1, Integer::sum);
    }

    public int count(T t) {
        return counts.getOrDefault(t, 0);
    }
}

Use like this:

final Counter<String> counts = new Counter<>();

counts.add("Test");
counts.add("Test");
counts.add("Other");
counts.add("Other");
counts.add("Other");

System.out.println(counts.count("Test"));
System.out.println(counts.count("Other"));
System.out.println(counts.count("Another"));

Output:

2
3
0
like image 179
Boris the Spider Avatar answered Nov 06 '22 00:11

Boris the Spider


Not as far as I know. But scala is very expressive, allowing you to cook something like it yourself:

def counts[T](s: Seq[T]) = s.groupBy(x => x).mapValues(_.length)

Edit: Even more concise with:

def counts[T](s: Seq[T]) = s.groupBy(identity).mapValues(_.length)
like image 39
Dave Avatar answered Nov 06 '22 01:11

Dave


Another scala version, doing it in one pass and avoiding .groupBy

val l = List("a", "b", "b", "c", "b", "c", "b", "d")

l.foldLeft(Map[String, Int]() withDefaultValue (0))
          { (m, el) => m updated (el, m(el)+1)}
//> res1: Map(a -> 1, b -> 4, c -> 2, d -> 1)

or if you don't want a map with default value zero

l.foldLeft(Map[String, Int]()) { (m, el) => m updated (el, m.getOrElse(el,0)+1)}
like image 31
The Archetypal Paul Avatar answered Nov 06 '22 02:11

The Archetypal Paul