1

Given a numeric string, I would like to apply an operation every nth digit in the string. Is this possible with java streams?

For example, for the string "12345" and applying a sum of 3 on every 2nd character, the result would be "15375".

public static void main(String[] args) {
    "12345".chars()
           .mapToObj(Character::getNumericValue)
           .mapToInt(c -> c + 3) //should only happen every 2nd character
           .forEach(System.out::print);
}

The above results in 45678 because the sum is applied to all characters.

Mark Rotteveel
  • 100,966
  • 191
  • 140
  • 197
  • Aside from a for loop being far more appropriate here, you would need to add some sort of index to the stream first, to allow you to 'know' which index you were taking it from. See here https://stackoverflow.com/questions/18552005/is-there-a-concise-way-to-iterate-over-a-stream-with-indices-in-java-8 – Thomas Timbul Jan 23 '22 at 16:13
  • What result do you expect if the input was `99999` ? `9129129` making the original string longer or `92929` keeping the length equal or ... ? – Eritrean Jan 23 '22 at 16:36
  • @Eritrean: I kept it simple just to show to focus on that particular step. There are other requirements and indeed one for what you are describing. In that case, if the result is > 9 we keep the original. – code_trigger Jan 23 '22 at 16:46

4 Answers4

1

I believe this is what you are looking for:

public static void main(String[] args) {
    String string = "12345";
    String result = IntStream.range(0, string.length())
            .mapToObj(index -> incrementCharAtEvenIndex(string, index, 3))
            .collect(Collectors.joining());

    System.out.println(result);
}
static String incrementCharAtEvenIndex(String string, int index, int increment) {
    int number = Character.getNumericValue(string.charAt(index));
    if ((index + 1) % 2 == 0) {
        number = number + increment;
    }
    return String.valueOf(number);
}

Unfortunately, Stream API is not built to work with the indices directly, so you have to use IntStream to simulate for-loop and a custom method to increment a numeric character if it appears at the even index (remember it starts at 0).

Nikolas Charalambidis
  • 40,893
  • 16
  • 117
  • 183
1

You can use the range operator to add a index to your stream:

String myString = "12345";
int n = 2;
        
IntStream
  .range(0, myString.length())
  .mapToObj(i -> Map.entry(i, Character.getNumericValue(myString.charAt(i))))
  .mapToInt(pair -> (pair.getKey() + 1) % n == 0 ? pair.getValue() + 3 : pair.getValue())
  .forEach(System.out::print);
Daniel Pucher
  • 179
  • 1
  • 5
1

You can do it like this.

String s = "12345";
String result = update(s, 2, 3);
System.out.println(result);

prints

15375
  • takes a string, skip value, and increment
  • use (i+1)%n to start with nth character.
  • use ternary operator (?:) to map to the sum depending on the remainder of the index to the skip value.
  • return new String
public static String update(String str, int n, int inc) {
    return IntStream.range(0, str.length())
            .mapToObj(i -> String.valueOf((i + 1) % n == 0 ?
                    str.charAt(i) - '0' + inc :
                        str.charAt(i) - '0'))
            .collect(Collectors.joining());
}   
WJS
  • 36,363
  • 4
  • 24
  • 39
0

I actually had a solution using indices but I was not sure if there was a way to avoid that. Apparently not.

This also works:

public static void main(String[] args) {
        String input = "12345";
        String collect = IntStream.range(0, input.length())
                                  .map(index -> {
                                      int value = getNumericValue(input.charAt(index));
                                      return (index % 2 != 0) ? value + 3 : value;
                                  })
                                  .mapToObj(String::valueOf)
                                  .collect(Collectors.joining());

        System.out.println(collect);
    }