DEV Community

Cover image for Daily Challenge #270 - Fix String Case
dev.to staff
dev.to staff

Posted on

Daily Challenge #270 - Fix String Case

In this challenge, you will be given a string that may have mixed uppercase and lowercase letters and your task is to convert that string to either lowercase only or uppercase only based on:

  • making as few changes as possible
  • if the string contains equal number of uppercase and lowercase letters, convert the string to lowercase.
For example:
solve("coDe") = "code". Lowercase characters > uppercase. Change only the "D" to lowercase.
solve("CODe") = "CODE". Uppercase characters > lowecase. Change only the "e" to uppercase.
solve("coDE") = "code". Upper == lowercase. Change all to lowercase.

Tests:
solve("code")
solve("CODe")
solve("COde")
solve("Code")

Good luck!


This challenge comes from KenKamau on CodeWars. Thank you to CodeWars, who has licensed redistribution of this challenge under the 2-Clause BSD License!

Want to propose a challenge idea for a future post? Email yo+challenge@dev.to with your suggestions!

Top comments (13)

Collapse
 
savagepixie profile image
SavagePixie

JavaScript

Something like this should do it:

const solve = str => str.replace(/[a-z]/g, '').length > str.length / 2
   ? str.toUpperCase()
   : str.toLowerCase()
Collapse
 
reinhart1010 profile image
Reinhart Previano K. • Edited

“A simple step for RegEx. A huge leap for a bunch of loops and ifs.”

Collapse
 
agtoever profile image
agtoever • Edited

Python solution. Uses helper function that counts the matching elements in a list. I use that to compare the string to itself converted to upper- and lowercase.

TIO link

def matching(l1, l2):
    # Returns the number of matching corresponding elements of l1 and l2
    return sum(e1 == e2 for e1, e2 in zip(l1, l2))

def solve(s):
    return s.lower() if matching(s, s.upper()) <= matching(s, s.lower()) else s.upper()

cases = ["coDe", "CODe", "coDE", "code", "CODe", "COde", "Code"]
for case in cases:
    print(f'{case} -> {solve(case)}')
Collapse
 
bb4l profile image
bb4L • Edited

in nim:

import strutils

proc solve*(s: string): string =
    var lower = 0
    var upper = 0
    var lowerResult = ""
    var upperResult = ""

    for c in s:
        if c.isLowerAscii():
            lowerResult.add(c)
            upperResult.add(c.toUpperAscii())
            inc(lower)
        else:
            lowerResult.add(c.toLowerAscii())
            upperResult.add(c)
            inc(upper)

    if lower >= upper:
        return lowerResult

    return upperResult

probably too verbose but that way I am only looping once through the string :-)

Collapse
 
ritheeshbaradwaj profile image
Ritheesh
def solve(inp):
    lower_count = 0
    length = len(inp)
    for i in inp:
        if i.islower():
            lower_count += 1
    if 2*lower_count >= length:
        return inp.lower()
    return inp.upper()

Tests:
solve("code") = "code"
solve("CODe") = "CODE"
solve("COde") = "code"
solve("Code") = "code"

Please let me know if there are any changes to be made.

Collapse
 
cipharius profile image
Valts Liepiņš

Haskell solution:

import Data.List (foldl')
import Data.Char (toLower, toUpper, isLower)

solve :: String -> String
solve str =
  if fromIntegral nLower / nTotal >= 0.5
    then toLower <$> str
    else toUpper <$> str
  where
    (nLower, nTotal) = foldl' counter (0,0) str
    counter (nLower, nTotal) ch =
      if isLower ch
        then (nLower+1, nTotal+1)
        else (nLower, nTotal+1)
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
Collapse
 
peter279k profile image
peter279k

Here is the simple solution without any advanced approach (e.g. ASCII code converting, regular expression and so on.):

function solve($s) {
  $upperCases = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
  $lowerCases = strtolower($upperCases);

  $upperCaseCount = 0;
  $lowerCaseCount = 0;

  $index = 0;

  for (; $index<strlen($s); $index++) {
    if (strpos($upperCases, $s[$index]) !== false) {
      $upperCaseCount += 1;
    }
    if (strpos($lowerCases, $s[$index]) !== false) {
      $lowerCaseCount += 1;
    }
  }

  if ($upperCaseCount > $lowerCaseCount) {
    return strtoupper($s);
  }

  if ($upperCaseCount <= $lowerCaseCount) {
    return strtolower($s);
  }
}
Collapse
 
peledzohar profile image
Zohar Peled

Is it really a challenge?

string solve(string input)
{
    return input.Count(c => char.IsUpper(c)) > input.Length / 2 
        ? input.ToUpper()
        : input.ToLower();
}
Collapse
 
pavi2410 profile image
Pavitra Golchha

Kotlin

fun solve(input: String): String
    = if (input.count { it.isUpperCase() } > input.length / 2) input.toUpperCase() else input.toLowerCase()
Collapse
 
bb4l profile image
bb4L

True story..

Your solution is nice and short. 👍

Honestly i don't know what's more "efficient".