28
\$\begingroup\$

Inspired by this challenge (or, more specifically, by misreading it), I've come up with the following challenge:

Given an input string S, reverse the order of all uppercase characters, and all lowercase characters. Leave all non-letter characters in place. For instance:

Hello, World!

Note that the uppercase W (the first uppercase letter) was replaced with H (the last). Same goes for the lowercase letters: 'd' (the first) is swapped with e (the last), l (second) gets replaced with l (pen-ultimate) ... All the non-letter characters are left in place.

Input

  • Input is a string with only ASCII characters in the range 32-126.
  • Input is guaranteed to be at least 1 character long, and won't exceed your language's limit.

Output

  • That same string, with the characters swapped as described.

Additional rules

  • Standard loopholes are forbidden
  • Answer must be a full program or a function, not a snippet or a REPL-entry.
  • , shortest answer in bytes wins.

Test cases

A
A

Ok
Ok

OK
KO

Hello, World!
Wdlro, Holle!

0123456789
0123456789

The quick brown Fox jumps over the lazy doge
Feg odyza lehtr Tev ospmu jxon wor bkci uqeh

odd
ddo

racecar
racecar

EtOn Em OsN R AaToNsIsIhT!!1!
ThIs Is NoT A RaNsOmEnOtE!!1!
\$\endgroup\$
2
  • \$\begingroup\$ You might want to include a 2-char testcase, my original solution failed on that at first. (Fixed at no cost by changing .+ to .*) \$\endgroup\$ Feb 11, 2017 at 15:27
  • \$\begingroup\$ "lazy doge" reminded me of this: youtube.com/watch?v=W-d6uUSY9hk \$\endgroup\$
    – FinW
    Feb 13, 2017 at 16:21

12 Answers 12

12
\$\begingroup\$

Retina, 19 bytes

Retina doesn't have a direct way to reverse a string, but we can do it by exploiting the sorting stage:

O^#`[a-z]
O^#`[A-Z]

Sort (O), reading them as numbers (#), and then reverse the ordering (^), of all the strings matching the given regex (lowercase letters for the first line, and uppercase letters for the second).

This works because when we try to read strings without numeric characters as numbers they get treated as 0, so all the characters have the same value for sorting. Since sorting is stable they are left in the same order, and reversing them returns the original string reversed.

Try it online!

\$\endgroup\$
10
\$\begingroup\$

Perl, 45 bytes

44 bytes of code + -p flag.

for$c(u,l){@T=/\p{L$c}/g;s/\p{L$c}/pop@T/ge}

Try it online!

Unicode characters classes \p{Lu} and \p{Ll} matches respectively uppercase and lowercase letters.
So /\p{L$c}/ will return the list of all upper (or lower) case letters (and store it inside @T).
And then, the regex s/\p{$c}/pop@T/ge will replace each (upper, then lower case) letter by the last letter of @T while removing it from @T.

\$\endgroup\$
7
\$\begingroup\$

JavaScript (ES6), 74 73 71 70 bytes

f=
s=>(g=r=>s=s.replace(r,_=>a.pop(),a=s.match(r)))(/[A-Z]/g,g(/[a-z]/g))
<input oninput=o.textContent=f(this.value)><pre id=o>

Edit: Saved 1 byte thanks to @Arnauld.

\$\endgroup\$
1
  • 4
    \$\begingroup\$ I knew there was a better way... \$\endgroup\$ Feb 11, 2017 at 16:45
6
\$\begingroup\$

MATL, 14 bytes

2:"t@Y2myy)Pw(

Try it at MATL Online

Explanation

        % Impicitly grab input as a string
2:      % Push the array [1, 2] to the stack
"       % For each value in this array
  t     % Duplicate the top element of the stack (S)
  @     % Get the current loop index
  Y2    % Load the predefined literal 1Y2 ('ABC...Z') on the first loop
        % and the predefined literal 2Y2 ('abc...z') on the second loop (M)
  m     % Create a logical array the length of S that is TRUE when a character is in the
        % array M and FALSE otherwise (B)
  yy    % Make a copy of both S and B
  )     % Grab just the letters of S that were in M using B as an index
  P     % Reverse this array
  w     % Flip the top two stack elements
  (     % Assign them back into the string
        % Implicit end of for loop and implicit display
\$\endgroup\$
1
  • 1
    \$\begingroup\$ Great job! I had 2:"tttXk>f)5MP(Yo for 17 bytes \$\endgroup\$
    – Luis Mendo
    Feb 11, 2017 at 16:10
5
\$\begingroup\$

JavaScript (ES6), 92 bytes

s=>(F=(r,s)=>s.replace(r,([x],a,y)=>y+F(r,a)+x))(/[a-z](.*)([a-z])/,F(/[A-Z](.*)([A-Z])/,s))

There has got to be a way to take advantage of the similarity between the regexes...

Test snippet

let f =
s=>(F=(r,s)=>s.replace(r,([x],a,y)=>y+F(r,a)+x))(/[a-z](.*)([a-z])/,F(/[A-Z](.*)([A-Z])/,s))
<input style="width:500px" value="Wdlro, Holle!" oninput="O.value=f(this.value)"><br>
<input style="width:500px" value="Hello, World!" id=O disabled>

\$\endgroup\$
5
  • \$\begingroup\$ Does this assume that the function is assigned to a variable called f? Shouldn;t that be in the byte count? \$\endgroup\$
    – steenbergh
    Feb 11, 2017 at 15:25
  • \$\begingroup\$ @steenbergh The function is anonymous, it can be called whatever you want it to be \$\endgroup\$
    – user41805
    Feb 11, 2017 at 15:25
  • 1
    \$\begingroup\$ @steenbergh Nope, it's an anonymous function which creates another function F then calls it recursively twice. The outer function doesn't actually call itself at any point. \$\endgroup\$ Feb 11, 2017 at 15:26
  • \$\begingroup\$ Why do you use parentheses around .* in the regexes? \$\endgroup\$
    – Luke
    Feb 11, 2017 at 15:41
  • \$\begingroup\$ @Luke to capture those characters (the a in ([x],a,y)=>) \$\endgroup\$ Feb 11, 2017 at 15:42
4
\$\begingroup\$

Perl 6, 75 69 bytes

{my @a=.comb;@(grep $_,@a).&{@$_=[R,] $_} for /<:Lu>/,/<:Ll>/;[~] @a}

How it works

  1. my @a=.comb;
    Split the string into characters, and store them in an array.

  2. for /<:Lu>/,/<:Ll>/
    For two regexes matching upper and lower-case letters, respectively...

    • @(grep $_,@a)
      Get a slice of all array entries matching the regex.

    • .&{@$_=[R,] $_}
      Assign the reverse of the slice to itself.

  3. [~] @a
    Concatenate the modified array to form a string again, and return it.


-6 bytes by stealing the idea to use Unicode classes instead of character ranges, from @Dada's solution.

\$\endgroup\$
3
\$\begingroup\$

Jelly, 14 bytes

nŒlT,Ṛ$yJịŒsµ⁺

Try it online!

How it works

nŒlT,Ṛ$yJịŒsµ⁺  Main link. Argument: s (string)

 Œl             Convert to lowercase.
n               Test for inequality.
   T            Truth; yield all indices of 1's.
    ,Ṛ$         Pair with its reverse. Yields [A, B] (pair of lists).
        J       Indices; yield I := [1, ..., len(s)].
       y        Translate; replace the integers of I that occur in A with the
                corresponding integers in B.
          Œs    Swapcase; yield s with swapped case.
         ị      Use the translated index list to index into s with swapped case.
            µ   Combine all links to the left into a chain.
             ⁺   Duplicate the chain, executing it twice.
\$\endgroup\$
3
  • \$\begingroup\$ not to be nitpicky but.. 14 characters != 23 bytes :) mothereff.in/byte-counter \$\endgroup\$
    – Gizmo
    Feb 13, 2017 at 12:26
  • \$\begingroup\$ @Gizmo Jelly uses a codepage. See this meta post for more information. \$\endgroup\$
    – Suever
    Feb 13, 2017 at 13:28
  • \$\begingroup\$ @Suever Oh that's neat, learned something today ^.^ \$\endgroup\$
    – Gizmo
    Feb 13, 2017 at 14:25
3
\$\begingroup\$

Bash + Unix utilities, 122 121 bytes

f()(p=[^$1*
v="\)\([$1\)\("
for((n=99;n;n--)){
q="$q;s/^\($p$v.*$v$p\)$/\1\4\3\2\5/"
p=[^$1*[$1$p
}
sed $q)
f a-z]|f A-Z]

Try it online!

Not really very short; maybe someone can golf it further.

Input on stdin, output on stdout.

This will work correctly on inputs of less than 200 characters.

(Actually it correctly handles any string with fewer than 200 lower-case letters and fewer than 200 upper-case letters.)

If you increase the 99 in the code to 102 (at the cost of one additional byte), it will handle strings up to 205 characters.

However, you can't increase the 99 in the code beyond 102 since you'll then exceed sed's maximum argument length.

Here's a version without any particular input size limitation, but the count is a little longer, 137 bytes. (This longer version writes to an auxiliary file named t.)

f()(p=[^$1*
v="\)\([$1\)\("
for((n=`wc -c<t`;n;n--)){
sed -i "s/^\($p$v.*$v$p\)$/\1\4\3\2\5/" t
p=[^$1*[$1$p
})
cat>t
f a-z]
f A-Z]
cat t

Test runs:

for x in A Ok OK 'Hello, World!' 0123456789 'The quick brown Fox jumps over the lazy doge' odd racecar 'EtOn Em OsN R AaToNsIsIhT!!1!'
  do
    echo "$x"
    ./swapping3 <<<"$x"
    echo
  done

A
A

Ok
Ok

OK
KO

Hello, World!
Wdlro, Holle!

0123456789
0123456789

The quick brown Fox jumps over the lazy doge
Feg odyza lehtr Tev ospmu jxon wor bkci uqeh

odd
ddo

racecar
racecar

EtOn Em OsN R AaToNsIsIhT!!1!
ThIs Is NoT A RaNsOmEnOtE!!1!
\$\endgroup\$
1
  • \$\begingroup\$ Interesting that it fails in TIO. ☹ May depend on the sed implementation installed on your system, but to GNU sed you can add -r option and remove the \ escaping of all parenthesis. \$\endgroup\$
    – manatwork
    Aug 9, 2019 at 8:47
2
\$\begingroup\$

Python 2, 115 bytes

s=input();u=str.isupper
exec"r='';i=0\nfor c in s:r+=c[u(c):]or filter(u,s)[~i];i+=u(c)\ns=r.swapcase();"*2
print s

Try it online!

\$\endgroup\$
2
  • \$\begingroup\$ Can you replace \n with ;? \$\endgroup\$
    – Tim
    Feb 11, 2017 at 17:51
  • \$\begingroup\$ Unfortunately, no. The argument of exec is parsed as usual Python code, so the for loop must be on its own line. \$\endgroup\$
    – Dennis
    Feb 11, 2017 at 18:03
2
\$\begingroup\$

Java (OpenJDK 8), 271 bytes

s->new String(new Error(){char[]o=s.toCharArray();char c;int b;{while(b++<2)for(int l=0,r=o.length;l<r;l++){for(--r;r>l&&f(r);r--);for(;l<r&&f(l);l++);if(l<r){o[l]=o[r];o[r]=c;}}}boolean f(int i){c=o[i];return b>1?!Character.isUpperCase(c):!Character.isLowerCase(c);}}.o)

Try it online!

\$\endgroup\$
3
  • \$\begingroup\$ You could save some bytes by making this into a lambda. s->new String... \$\endgroup\$ Feb 12, 2017 at 2:40
  • 1
    \$\begingroup\$ @NonlinearFruit thank you! 294 -> 272, also fixed mistake when r an l was reused without initialization. \$\endgroup\$ Feb 12, 2017 at 7:03
  • \$\begingroup\$ Welcome to PPCG! Some things you could still golf: char[]o=s.toCharArray();char c;int b; to char o[]=s.toCharArray(),c,b;; and both && to &'; and c=o[i];return b>1?!Character.isUpperCase(c):!Character.isLowerCase(c); to c=o[i];Character x=c;return b>1?!x.isUpperCase(c):!x.isLowerCase(c); (259 bytes in total). And I probably missed some things to golf it more. Also, if you haven't seen it yet, tips for golfing in Java might be interesting to read. \$\endgroup\$ Feb 15, 2017 at 8:57
1
\$\begingroup\$

R, 107 bytes

u=utf8ToInt(scan(,''));for(i in c(65,97)){l=which(u%in%i:(i+25));u[l]=u[rev(l)]};cat(intToUtf8(u,T),sep="")

Adapted from my response to the linked challenge. This is considerably easier than just swapping pairs. I wonder if I could get sub 100 with some golfs...

Try it online!

\$\endgroup\$
1
\$\begingroup\$

05AB1E, 19 bytes

2FÐlø€Ë≠āsƶ0K‡<è.š

I/O as a list of characters.
Based on my 05AB1E answer in the related challenge.

Try it online or verify all test cases.

Explanation:

2F            # Loop 2 times:
  Ð           #  Triplicate the current character-list
              #  (which will use the implicit input-list in the first iteration)
   l          #  Lowercase all letters of the top copy
    ø         #  Zip/transpose the top two lists; pairing them together, and swapping
              #  rows/columns
     €Ë≠      #  Check for each pair whether they're NOT the same
              #  (so uppercase characters that have now become lowercase)
  ā           #  Push a list in the range [1, length] (without popping)
   s          #  Swap so the mapped list is at the top again
    ƶ         #  Multiply each 1/0 by its 1-based index
     0K       #  Remove all 0s
       Â      #  Bifurcate it (short for Duplicate & Reverse copy)
        ‡     #  Transliterate the indices with the reversed indices in [1, length]
         <    #  Decrease each by 1 (because 05AB1E uses 0-based indexing)
          è   #  Index them into the current character-list
           .š #  And swap all cases (lowercase to uppercase, and vice-versa)
              # (after the loop, the resulting character-list is output implicitly)
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.