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\$
  • \$\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\$ – ETHproductions Feb 11 '17 at 15:27
  • \$\begingroup\$ "lazy doge" reminded me of this: youtube.com/watch?v=W-d6uUSY9hk \$\endgroup\$ – FinW Feb 13 '17 at 16:21

11 Answers 11

5
\$\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
    \$\begingroup\$ Great job! I had 2:"tttXk>f)5MP(Yo for 17 bytes \$\endgroup\$ – Luis Mendo Feb 11 '17 at 16:10
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\$
10
\$\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\$
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\$
  • 4
    \$\begingroup\$ I knew there was a better way... \$\endgroup\$ – ETHproductions Feb 11 '17 at 16:45
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\$
  • \$\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 '17 at 15:25
  • \$\begingroup\$ @steenbergh The function is anonymous, it can be called whatever you want it to be \$\endgroup\$ – Cows quack Feb 11 '17 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\$ – ETHproductions Feb 11 '17 at 15:26
  • \$\begingroup\$ Why do you use parentheses around .* in the regexes? \$\endgroup\$ – Luke Feb 11 '17 at 15:41
  • \$\begingroup\$ @Luke to capture those characters (the a in ([x],a,y)=>) \$\endgroup\$ – ETHproductions Feb 11 '17 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\$
2
\$\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\$
  • \$\begingroup\$ not to be nitpicky but.. 14 characters != 23 bytes :) mothereff.in/byte-counter \$\endgroup\$ – Gizmo Feb 13 '17 at 12:26
  • \$\begingroup\$ @Gizmo Jelly uses a codepage. See this meta post for more information. \$\endgroup\$ – Suever Feb 13 '17 at 13:28
  • \$\begingroup\$ @Suever Oh that's neat, learned something today ^.^ \$\endgroup\$ – Gizmo Feb 13 '17 at 14:25
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\$
  • \$\begingroup\$ Can you replace \n with ;? \$\endgroup\$ – Tim Feb 11 '17 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 '17 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\$
  • \$\begingroup\$ You could save some bytes by making this into a lambda. s->new String... \$\endgroup\$ – NonlinearFruit Feb 12 '17 at 2:40
  • 1
    \$\begingroup\$ @NonlinearFruit thank you! 294 -> 272, also fixed mistake when r an l was reused without initialization. \$\endgroup\$ – DmitrySamoylenko Feb 12 '17 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\$ – Kevin Cruijssen Feb 15 '17 at 8:57
2
\$\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\$
  • \$\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 at 8:47
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\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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