DEV Community

Discussion on: Daily Challenge #270 - Fix String Case

Collapse
 
willsmart profile image
willsmart

Here's a C implementation that does a pretty good job at being as lazy as possible (which is what I always aspire to, even if it ends up being a lot of work to get to be lazy)

It updates the string in-place, passing through only once if nothing is to be done, or if its original guess of the final case was right. Otherwise, it passes through again only up to the last char it guessed wrong on.
Worst case, it iterates the string twice (minus one last character on the second pass), but the typical case is more like a single pass.

In C for old-times sake, and because if you're bothering to do this sort of thing at all it'd better be in a performant language just to make any of it worth the effort.

#include <stdio.h>
#include <string.h>

#define DEB(...) __VA_ARGS__

#define ascii_A 65
#define ascii_Z 90
#define ascii_case_bitmask 0x20
#define isUpperCase(___c) (((___c) & ascii_case_bitmask) == 0)
#define toggleCase(___c) ((___c) ^= ascii_case_bitmask)

void convertToPopularCase(char *s) {
    int stat_toggleCount = 0, stat_stringLength = (int)strlen(s);
    printf(">>> %s\n", s);

    int lastWrongIndex = -1, upperCount = 0, lowerCount = 0;
    char upperFTW = 0;
    for (int i=0; s[i]; i++) {
        const char c = s[i], upperC = c & ~ascii_case_bitmask, isUpper = isUpperCase(c);
        if (upperC < ascii_A || upperC > ascii_Z) continue; // non-letters are ignored
        upperCount += isUpper;
        lowerCount += 1 - isUpper;
        if (upperFTW != upperCount > lowerCount) {
            lastWrongIndex = i - 1;
            upperFTW = !upperFTW;
        }
        if (isUpper != upperFTW) {
            toggleCase(s[i]); // toggle case of char in string
            stat_toggleCount++;
        }
    }
    for (int i=0; i <= lastWrongIndex; i++) {
        const char c = s[i], upperC = c & ~ascii_case_bitmask, isUpper = isUpperCase(c);
        if (upperC < ascii_A || upperC > ascii_Z) continue; 
        if (isUpper != upperFTW) {
            toggleCase(s[i]); // toggle case of char in string
            stat_toggleCount++; 
        }
    }

    printf(" << %s\n    (Length: %d, chars toggled: %d, total string passes including cleanup: %f\n\n", s, stat_stringLength, stat_toggleCount, (stat_stringLength + (lastWrongIndex + 1))/(float)stat_stringLength);
}

int main()
{
    char testStrings[][100] = {
        "Following examples should be uppercase>>>",
        "CODE",
        "CODe",
        "cODE",
        "A MoNkEy",
        "123456789 CODE",
        "four - FIVEE + four - FIVEE",
        "four + fivee - FOUR - SIXXXX",

        "Following examples should be lowercase>>>",
        "code",
        "COde", "Code",
        "coDE", "codE",
        "MoNkEy",
        "123456789 code", "123456789 coDE",
        "four + fivee - FOUR - FIVEE",
    };

    for (int i=0, N = sizeof(testStrings)/sizeof(*testStrings); i<N; i++) {
        convertToPopularCase(testStrings[i]);
    }

    return 0;
}

Output:
(a "total string passes including cleanup" of 1 is where the string was just passed over once)

>>> Following examples should be uppercase>>>
 << following examples should be uppercase>>>
    (Length: 41, chars toggled: 1, total string passes including cleanup: 1.024390

>>> CODE
 << CODE
    (Length: 4, chars toggled: 0, total string passes including cleanup: 1.000000

>>> CODe
 << CODE
    (Length: 4, chars toggled: 1, total string passes including cleanup: 1.000000

>>> cODE
 << CODE
    (Length: 4, chars toggled: 3, total string passes including cleanup: 1.500000

>>> A MoNkEy
 << A MONKEY
    (Length: 8, chars toggled: 3, total string passes including cleanup: 1.000000

>>> 123456789 CODE
 << 123456789 CODE
    (Length: 14, chars toggled: 0, total string passes including cleanup: 1.714286

>>> four - FIVEE + four - FIVEE
 << FOUR - FIVEE + FOUR - FIVEE
    (Length: 27, chars toggled: 22, total string passes including cleanup: 1.925926

>>> four + fivee - FOUR - SIXXXX
 << FOUR + FIVEE - FOUR - SIXXXX
    (Length: 28, chars toggled: 27, total string passes including cleanup: 1.964286

>>> Following examples should be lowercase>>>
 << following examples should be lowercase>>>
    (Length: 41, chars toggled: 1, total string passes including cleanup: 1.024390

>>> code
 << code
    (Length: 4, chars toggled: 0, total string passes including cleanup: 1.000000

>>> COde
 << code
    (Length: 4, chars toggled: 4, total string passes including cleanup: 1.750000

>>> Code
 << code
    (Length: 4, chars toggled: 1, total string passes including cleanup: 1.250000

>>> coDE
 << code
    (Length: 4, chars toggled: 2, total string passes including cleanup: 1.000000

>>> codE
 << code
    (Length: 4, chars toggled: 1, total string passes including cleanup: 1.000000

>>> MoNkEy
 << monkey
    (Length: 6, chars toggled: 3, total string passes including cleanup: 1.833333

>>> 123456789 code
 << 123456789 code
    (Length: 14, chars toggled: 0, total string passes including cleanup: 1.000000

>>> 123456789 coDE
 << 123456789 code
    (Length: 14, chars toggled: 2, total string passes including cleanup: 1.000000

>>> four + fivee - FOUR - FIVEE
 << four + fivee - four - fivee
    (Length: 27, chars toggled: 9, total string passes including cleanup: 1.000000