loading...
The DEV Team

Daily Challenge #34 - WeIrD StRiNg CaSe

thepracticaldev profile image dev.to staff ・1 min read

The goal of this challenge is to write a function that accepts a string and returns the same string with all even indexed characters uppercased and all odd indexed characters lowercased.

This indexing should be zero-based with the index in position zero being considered even. The input string will consist of only alphabetical characters and spaces. Spaces should only be present when there are multiple words.

Example:
to_weird_case('String'); # => returns 'StRiNg'
to_weird_case('Weird string case') # => returns 'WeIrD StRiNg CaSe'

Happy coding~!


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

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

Posted on by:

thepracticaldev profile

dev.to staff

@thepracticaldev

The hardworking team behind dev.to ❤️

The DEV Team

The team behind this very platform. 😄

Discussion

markdown guide
 

Here goes a simple oneliner:

toWeirdCase=(s)=>[...s].map((e,i)=>i%2?e.toLowerCase():e.toUpperCase()).join('');

And the result:

toWeirdCase('String'); // "StRiNg"
toWeirdCase('Weird string case'); // "WeIrD StRiNg cAsE"
 

I believe case should be printed as CaSe as first letter (index 0) should be always capitalized.

 

Indeed! Didn't catch that! Here is a little fix for that:

toWeirdCase=(a)=>a.split` `.map(s=>[...s].map((e,i)=>i%2?e.toLowerCase():e.toUpperCase()).join``).join` `;

which output is:

toWeirdCase('String'); // "StRiNg"
toWeirdCase('Weird string case'); // "WeIrD StRiNg CaSe"
 

Index 0 refers to the first letter of the entire string; we don't need to consider each word separately, just the string as a whole

Sorry about that.
I meant it as index 0 of each word.

the example shows the result should be

to_weird_case('Weird string case') # => returns 'WeIrD StRiNg CaSe'

Ah, you're right, that's interesting. I hadn't noticed that discrepancy with the capital 'C' in 'CaSe'; that makes this a more interesting challenge!

The original post should probably call that out a little more clearly, because it raises additional questions: Should punctuation count as a word separator? e.g., should 'word-other' become 'WoRd-oThEr' or 'WoRd-OtHeR'? Or should we only worry about letters and spaces? What characters are we considering?

@thepracticaldev , any help here?

For some reason, CodeWars isn't loading for me so won't be able to check the edge cases... 🤔

 

In Js ( Many Optimised solutions are already given, just tried different way)

const to_weird_case = (string) => {
    let  newString = [];
    for( let i in string ) {
        if(i%2 == 0){
            newString.push(string[i].toUpperCase());
        } else {
            newString.push(string[i].toLowerCase());
        }
    }
    return newString.join('');
};

to_weird_case('Weird string case');
 

Ruby, not optimal 🙃

def to_weird_case(string)
  if string.include? " "
    string.split(" ").map { |word| word.split("") }.each do |letter_array|
      letter_array.each_with_index do|letter, index|
        letter.upcase! if index % 2 == 0 }}.map(&:join).join(" ")
      end
    end
  else
    string.split("").each_with_index do |letter, index|
      letter.upcase! if index % 2 == 0
    end.join
  end
end
 

Python solution:

toWeirdCase = lambda s: ''.join([c.lower() if i%2 else c.upper() for i,c in enumerate(s)])

print(toWeirdCase('String')) #=> StRiNg
print(toWeirdCase('Weird string case')) #=> WeIrD StRiNg cAsE

EDIT
@dance2die pointed out in this comment that the multi-word example in the post seems to suggest that each word should be considered and cased separately, rather than the whole string. In that case, this becomes a bit more interesting.

Here's a simple modification to account only for spaces separating words:

toWeirdCase = lambda s: ' '.join([wordToWeirdCase(w) for w in s.split(' ')])
wordToWeirdCase = lambda s: ''.join([c.lower() if i%2 else c.upper() for i,c in enumerate(s)])

print(toWeirdCase('String')) #=> StRiNg
print(toWeirdCase('Weird string case')) #=> WeIrD StRiNg CaSe

But this could get a lot more complex if we need to account for things like punctuation and other whitespace characters.

As one last example, here's one that handles alternative whitespace characters using regular expressions:

import re

toWeirdCase = lambda s: (
  s[:re.search(r'\S', s).start() if re.match(r'\s', s) else 0] +   # handle leading whitespace
  ''.join(
    [wordToWeirdCase(w) for w in re.findall(r'\S+(?:\s+)?', s)]
  )
)

wordToWeirdCase = lambda s: ''.join([c.lower() if i%2 else c.upper() for i,c in enumerate(s)])

print(toWeirdCase('String')) #=> 'StRiNg'
print(toWeirdCase('Weird string case')) #=> 'WeIrD StRiNg CaSe'
print(toWeirdCase('  hELLO world\tHOW\'S EVeryTHInG???')) #=> '  HeLlO WoRlD   HoW'S EvErYtHiNg???'
 

JavaScript

function formatText(message) {
    return message.split(" ")
                  .map(function(el) {
                    return el.split('')
                             .map(function(letter, idx) {
                               return idx % 2 === 0 ? letter.toLowerCase() : letter.toUpperCase();
                             })
                             .join('');
                  })
                  .join(' ')
  }

And as an extension, I used that function to create a Sponge Bob Mocking meme generator! (which is the first thing that I thought when I saw the challenge)

 

At work, we just call this serial killer case :P

 

A simple solution in Javascript:

function toWeirdCase(string) {
  return string.split(' ')
    .map(word => {
      return word.split('')
        .map((char, index) => {
          if (index % 2 === 0) return char.toUpperCase()
          return char
        })
        .join('')
    })
    .join(' ')
}

console.log(toWeirdCase('string'))
console.log(toWeirdCase('Weird string  case'))
 

Here is the simple solution with PHP:

function toWeirdCase($string) {
    $strings = explode(' ', $string);

    $stringsIndex = 0;
    foreach ($strings as $string) {
      $index = 0;
      for (; $index < strlen($string); $index++) {
        if ($index % 2 === 0) {
          $string[$index] = strtoupper($string[$index]);
        } else {
          $string[$index] = strtolower($string[$index]);
        }
      }

      $strings[$stringsIndex] = $string;
      $stringsIndex++;
    }

    return implode(' ', $strings);
}
 

Heres my ruby example

def to_weird_case (str)
  raise "Incorrect input, expected String" if str.class != String
  str.split(/\s/).map{|w| w.chars.each_with_index.map{|c,i| i%2==0 ? c.upcase : c }.join }.join(' ')
end

result

to_weird_case('string') => "StRiNg"
to_weird_case('Weird string case') => "WeIrD StRiNg CaSe"
 

Here's mine -- I'll admit that it took a little to recognize that the index was supposed to be word-based, not for the entire string. But that's what test cases are for! :-)

const TRANSFORMER = {
    LOWER: String.prototype.toLowerCase,
    UPPER: String.prototype.toUpperCase
};

const transform = (v, which) => which.apply(v);

const weirdCase = str => 
 str.split(" ")
    .map((part) => Array.from(part)
                        .map((ch, idx) => transform(ch, idx & 1 ? TRANSFORMER.LOWER 
                                                                : TRANSFORMER.UPPER ))
                        .join(""))
    .join(" ");

Gist: gist.github.com/kerrishotts/e3a213...

 

Ruby solution. Doesn’t use modulo.

Works with the strange edge case. Also sorry typed on tablet.

str = "Weird ,string case."

class << str
  def to_weird_case
    String.new.tap do |s|
      i = 0

      while i < self.length
        # work out with edge case
        if self[i] =~ /[^a-zA-Z]/
          s << self[i]
          i += 1
          next
        end

        # we add to_s because we may be out of bounds;
        # that way we get the empty string instead of nil
        # and an exception
        s << self[i].to_s.upcase
        s << self[i + 1].to_s.downcase
        i += 2
      end
    end
  end
end

str.to_weird_case
 
def to_weird_case(s) s.split(' ').map { @1.chars.map.with_index
 { @2.even? ? @1.upcase : @1.downcase }.join }.join(' ') end
 

Ah I tried map_with_index but that's not a method. Did not know there was a .map.with_index!

 

I'm glad to hear that. Enumerable has so many methods, which is useful but I take a little bit time to remember them.

 

Solution in Dart. Simple and not the shortest one, I guess. But it works =)

toWeirdCase(String str) {
  var result = new List();

  for (int i = 0; i < str.length; i++) {
    result.add(i%2 == 0 ? str[i].toUpperCase() : str[i].toLowerCase());
  }
  return result.join();
}

Link to the playground: dartpad.dartlang.org/f01143ca792ed...

 

PHP 💻

<?php

/**
 * Daily Challenge #34 - WeIrD StRiNg CaSe
 * @param  string $string
 * @return string
 */
function toWeirdCase(string $string): string
{
  return implode(" ",array_map(function($word) {
    $splitWord = str_split($word);
    return implode("", array_map(function($value, $key) {
      return ($key % 2 === 0) ? strtoupper($value) : strtolower($value);
    }, $splitWord, array_keys($splitWord)));

  }, explode(" ", $string)));
}


echo toWeirdCase('Weird string case');
// Output: WeIrD StRiNg CaSe

 

Haskell:

import Data.Char (toLower, toUpper)
import Control.Applicative

toWeirdCase :: String -> String
toWeirdCase = unwords . map weirdifier . words
              where weirdifier = getZipList
                               . (ZipList [if even i then toUpper else toLower | i <- [0..]] <*>)
                               . ZipList

I used applicative functors here mostly for fun. I think it looks nicer to define weirdifier as

weirdifier = zipWith (\i -> if even i then toUpper else toLower) [0..]
 

My solution in js

const toWeirdCase = (str = '') => (str.split(' ').map((s) => ([...s].map((char, index) => (index % 2) ? char.toLowerCase() : char.toUpperCase())).join(''))).join(' ');
 

I've implemented based off of La blatte's answer.

const caser = {
  0: c => c.toUpperCase(),
  1: c => c.toLowerCase()   
}
const mod = i => i % 2
const weirdCase = text => text.split(' ').map(word => 
    [...word].map((c, i) => caser[mod(i)](c)).join('')
).join(' ')

console.log(["String", "Weird string case"].map(weirdCase))

prints,

["StRiNg", "WeIrD StRiNg CaSe"]
 
def weird_string (string) :
    result = ""
    for index in range(len(string)) :
        if index % 2 == 0 :
            result += string[index].upper()
        else :
            result += string[index].lower()
    return result

print (weird_string("Weird string case"))

Python one liner


def weird_string (string) :
    result = ("").join([string[i].lower() if i%2 else string[i].upper() for i in range(len(string))])
    return result

print(weird_string("Weird string case"))

 

Elixir:

defmodule Case do
  require Integer

  def weird(string) do
    string
    |> String.graphemes()
    |> Enum.with_index()
    |> Enum.map(fn
      {char, i} when Integer.is_even(i) -> String.upcase(char)
      {char, _} -> String.downcase(char)
    end)
    |> Enum.join()
  end
end
 

Python with a function :

def to_weird_case(string):
    weird_string = ''
    i = 0
    for c in string.lower():
        if i%2:
            weird_string += c
        else:
            weird_string += c.upper()
        i += 1
    return weird_string

One liner with list comprehension :

weird_case = ''.join([string[i].lower() if i%2 else string[i].capitalize() for i in range(0,len(string))])

 

Hey! We loved this challenge and actually got three developers to live code a solution in 15 minutes (with commentary): youtu.be/Z1fQ7J5fZhY

We also release similar videos every week on other Javascript, HTML/CSS, Python and Ruby challenges, so don't forget to like and subscribe if you're interested! ♥️

 

Here is a little Haskell one-liner

import Data.Char (toUpper, toLower)

toWeirdCase :: String -> String
toWeirdCase = unwords . map (zipWith ($) (cycle [toUpper, toLower])) . words