Kotlin Pipeline Example

An example using Kotlin, as at 1.0.6, to show a “functional” pipeline that transforms the input text resulting in a Map associating the character to its occurrence.

This simple Kotlin example will be compared to an imperative example and a Java 8 stream implementation.

 val input = "Mississippi"

 val result = input.groupBy {it}.mapValues { it.value.size }

 println("result = ${result}")
 result = {M=1, i=4, s=4, p=2}

The two stages in the pipeline are grouping and transforming, separated by the infix syntax, implemented in the Kotin Standard Library. Each operation produces a collection.

updated group and fold proposal for a more efficient pipeline

An imperative implementation, or a Fold operator, would typically create one result Map and not require an intermediate list for grouping.

This shows that the Kotlin groupBy operation will always use a list associated with each key.

public inline fun <K> String.groupByTo(map: MutableMap<K, MutableList<Char>>,
                                       toKey: (Char) -> K): Map<K, MutableList<Char>> {
    for (element in this) {
        val key = toKey(element)
        val list = map.getOrPut(key) { ArrayList<Char>() }
        list.add(element)
    }
    return map
}

This shows that the Kotlin mapValue operation will transform the value associated with each key in a destination map using the provided function.

public inline fun <K, V, R, C : MutableMap<K, R>> Map<K, V>.mapValuesTo(destination: C,
                                                            transform: (Map.Entry<K, V>) -> R): C {
    for (e in this) {
        val newValue = transform(e)
        destination.put(e.key, newValue)
    }
    return destination
}

Kotlin version - imperative


 var result = linkedMapOf<Char, Int>()

 for(char in input) {
   val count  = result.getOrPut(char, {0})
   result.put(char, count.plus(1))
 }

 println("result = ${result}")

Java 8 version - default collector is based on Hashmap - changed to LinkedHashMap

 public static <T, K, A, D> Collector<T, ?, Map<K, D>> inputGroupingBy(Function<? super T,
                         ? extends K> classifier, Collector<? super T, A, D> downstream) {
    return Collectors.groupingBy(classifier, LinkedHashMap::new, downstream);
  }

  public static void main(String[] args) {

    String input = "Mississippi";

    Map<Character, Long> result = input.chars()
               .mapToObj(c -> (char) c)
               .collect(inputGroupingBy(c -> c, Collectors.counting()));

    System.out.println(result);

  }

Alternative library support with Guava Multiset - counting letters

  HashMultiset<Character> letterFrequency = HashMultiset.create(Lists.charactersOf(input));
  lettersFrequency.count("s");