Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Fold operation in Scala Vs Java 8

How to perform the below Scala operation to find the most frequent character in a string in java 8?

val tst = "Scala is awesomestttttts"
val op  = tst.foldLeft(Map[Char,Int]())((a,b) => {
    a+(b -> ((a.getOrElse(b, 0))+1))
  }).maxBy(f => f._2)

Here the output is

(Char, Int) = (t,6)

I was able to get a stream of characters in Java 8 like this:

Stream<Character> sch = tst.chars().mapToObj(i -> (char)i);

but not able to figure out whats the fold/foldLeft/foldRight alternative we have in Java 8

Can someone pls help?

like image 457
Anand Avatar asked Feb 21 '16 07:02

Anand


2 Answers

Something like this seems to match with the Scala code you provided (if I understand it correctly):

String tst = "Java is awesomestttttts";
Optional<Map.Entry<Character, Long>> max =
    tst.chars()
       .mapToObj(i -> (char) i)
       .collect(Collectors.groupingBy(Function.identity(),
                                      Collectors.counting()))
       .entrySet()
       .stream()
       .max(Comparator.comparing(Map.Entry::getValue));
System.out.println(max.orElse(null));
like image 105
JB Nizet Avatar answered Nov 18 '22 23:11

JB Nizet


If you don't mind using a third-party library Eclipse Collections has a Bag type that can keep track of the character counts. I've provided two examples below that use Bags. Unfortunately there is no maxByOccurrences available today on Bag, but the same result can be achieved by using topOccurrences(1) which is available. You can also use forEachWithOccurrences to find the max but it will be a little more code.

The following example uses a CharAdapter, which is also included in Eclipse Collections.

MutableBag<Character> characters =
    CharAdapter.adapt("Scala is awesomestttttts")
        .collect(Character::toLowerCase)
        .toBag();
MutableList<ObjectIntPair<Character>> charIntPairs = characters.topOccurrences(2);

Assert.assertEquals(
    PrimitiveTuples.pair(Character.valueOf('t'), 6), charIntPairs.get(0));
Assert.assertEquals(
    PrimitiveTuples.pair(Character.valueOf('s'), 5), charIntPairs.get(1));

The second example uses the chars() method available on String which returns an IntStream. It feels a bit awkward that something called chars() does not return a CharStream, but this is because CharStream is not available in JDK 8.

MutableBag<Character> characters =
    "Scala is awesomestttttts"
        .toLowerCase()
        .chars()
        .mapToObj(i -> (char) i)
        .collect(Collectors.toCollection(Bags.mutable::empty));
MutableList<ObjectIntPair<Character>> charIntPairs = characters.topOccurrences(2);

Assert.assertEquals(
    PrimitiveTuples.pair(Character.valueOf('t'), 6), charIntPairs.get(0));
Assert.assertEquals(
    PrimitiveTuples.pair(Character.valueOf('s'), 5), charIntPairs.get(1));

In both examples, I converted the characters to lowercase first, so there are 5 occurrences of 's'. If you want uppercase and lowercase letters to be distinct then just drop the lowercase code in both examples.

Note: I am a committer for Eclipse Collections.

like image 8
Donald Raab Avatar answered Nov 18 '22 21:11

Donald Raab