Convert string to mixed case

Posted on

Problem

This method converts a string to mixed case, id est mixedCase("hello, world!") -> "HeLlO, wOrLd!".

// Any input string is lower case already
private static String mixedCase(String input) {

    // getAsCharacterList() simply turns a string into a List of Characters. 
    List<Character> charactersAsList = getAsCharacterList(input);
    StringBuilder result = new StringBuilder();
    int index = 0;

    for (Character character : charactersAsList) {
        if (Character.isLetter(character)) {
            if (index % 2 == 0) {
                result.append(Character.toUpperCase(character));
            } else {
                result.append(character);
            }
            index++;
        } else {
            result.append(character);
        }
    }

    return result.toString();
}

It works, but admittedly it looks terrible and I have difficulties making it more concise. I would have preferred to use streams if I didn’t had to reference indices…

Solution

Is there a more concise way to write this Java string converter?

Sure. No need for the intermediary charactersAsList variable, nor the getAsCharacterList method.

Since you know in advance the final length of the StringBuilder,
it’s good to pass that to the constructor when initializing.

// input is already lowercase
private static String mixedCase(String input) {
    StringBuilder result = new StringBuilder(input.length());
    int index = 0;

    for (char character : input.toCharArray()) {
        if (Character.isLetter(character)) {
            if (index % 2 == 0) {
                result.append(Character.toUpperCase(character));
            } else {
                result.append(character);
            }
            index++;
        } else {
            result.append(character);
        }
    }

    return result.toString();
}

Borrowing from the idea of @BrainFRZ to set the already lowercased input in StringBuilder, and from @ferada’s comment to use a boolean to decide to uppercase or not, here’s another variation that has some advantages:

private static String mixedCase(String input) {
    StringBuilder result = new StringBuilder(input);
    int index = 0;
    boolean toUpper = true;

    for (char character : input.toCharArray()) {
        if (Character.isLetter(character)) {
            if (toUpper) {
                character = Character.toUpperCase(character);
            }
            toUpper = !toUpper;
        }
        result.setCharAt(index, character);
        index++;
    }

    return result.toString();
}

This takes advantage of the input being already lowercased,
and naturally letting StringBuilder to use that.
With some of the conditions eliminated, this version is slightly more compact.

There’s actually a really easy way of doing this with a StringBuilder! All you need to do is set up a new StringBuilder and use its build-in setCharAt method.

public class StringTest {
    private static String mixedCase(String input) {
        StringBuilder sb = new StringBuilder(input.toLowerCase());

        for (int i = 0; i < input.length(); i += 2) {
            sb.setCharAt(i, Character.toUpperCase(input.charAt(i)));
        }

        return sb.toString();
    }

    public static void main(String[] args) {
        System.out.println(mixedCase("testing")); //--> TeStInG
    }
}

This way you don’t have to do any fun with modulo operations, collections, or any of that stuff. If you want to have it mixed the other way, you can start the for-loop at i = 1 for “tEsTiNg”.

Addendum:

As discussed in the comments, this solution only applies if you want the case to alternate every other letter based on its position in the string as opposed to alternating the case of every other letter ignoring non-letters.

Here’s another variation with stream:

public class Converter {
    public static void main(String[] args) {
        System.out.println(mixedCase("hello, world"));
    }

    public static String mixedCase(String s) {
        return IntStream.range(0, s.length())
                .mapToObj(i -> i % 2 == 0 ? Character.toUpperCase(str.charAt(i)) : str.charAt(i))
                .collect(StringBuilder::new, StringBuilder::appendCodePoint, StringBuilder::append)
                .toString();
    }
}

Leave a Reply

Your email address will not be published. Required fields are marked *