'How to do this string pattern aaabbc to be displayed as 3a2b1c in java 8 using streams

I want to display the String pattern aaabbcc to be displayed as 3a2b2c in java8 using streams

I have a working example in lower versions

String str = "aabbcccddd";

String chars[] = str.split("");
Map<String, Integer> compressMap = new LinkedHashMap<String, Integer>();
for(String s: chars) {
if("".equals(s))
    continue;
Integer count = compressMap.get(s);
if(count != null)
    compressMap.put(s, ++count);
else
    compressMap.put(s, 1);
}

StringBuffer output = new StringBuffer("");
for (Map.Entry<String, Integer> entry : compressMap.entrySet()) {
    output.append(entry.getValue()).append(entry.getKey());
}
System.out.println(output);

can anyone help on this?



Solution 1:[1]

What you did will still work on Java 8, but you can simplify your code by relying on Stream as next:

String str = "aabbcccddd";
// Convert the String into a Stream
// Convert int into Character
// Group by Character and count occurrences
// For each entry add the key (Character) followed by the value (Occurrences) to the result
// using the joining collector
String output = str.chars()
    .mapToObj(i -> (char)i)
    .collect(groupingBy(Function.identity(), LinkedHashMap::new, counting()))
    .entrySet()
    .stream()
    .flatMap(entry -> Stream.of(entry.getValue().toString(), entry.getKey().toString()))
    .collect(Collectors.joining());

System.out.println(output);

Output:

2a2b3c3d

Another approach slightly different where we convert Integer to char at the end:

String output = str.chars()
    .boxed()
    .collect(groupingBy(Function.identity(), LinkedHashMap::new, counting()))
    .entrySet()
    .stream()
    .flatMap(entry -> Stream.of(entry.getValue().toString(), Character.toString((char) entry.getKey().intValue())))
    .collect(Collectors.joining());

NB: I use LinkedHashMap::new as supplier instead of the default HashMap::new to preserve the order.

Solution 2:[2]

The other solution will be:

String output = Pattern.compile("(?<=(\\w))(?!\\1)")
        .splitAsStream(string)
        .map(part -> part.length() + part.substring(0, 1))
        .collect(Collectors.joining());

The Pattern (?<=(\\w))(?!\\1) match places in String where next char is different than previous, so it split String into parts containing separate letters, then map it to string with its length.

Solution 3:[3]

Here is the code for above problem: Time complexity: O(n) Space complexity: O(1)

    private void printCharacterLength(char[] arr) {
    StringBuilder result = new StringBuilder();
    int count = 1;
    for (int i = 1; i < arr.length; i++) {
        if (arr[i] == arr[i - 1]) {
            count++;
        } else {
            result.append((count > 1 ? count : "")).append(arr[i - 1]);
            count = 1;
        }
    }
    result.append((count > 1 ? count : "")).append(arr[arr.length - 1]);
    System.out.println(result);
}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 m.cekiera
Solution 3 user11016