53
\$\begingroup\$

Goal

This is a simple challenge. Your goal is to unscramble a string by swapping each letter with the next letter of the same case, while leaving non-letter characters unchanged.

example

Step by step explanation

  1. The first character is a E. We look for the next letter in upper case: it's a C. We swap these characters, which leads to CdoE!.

  2. We advance to the next character: this is a d. We look for the next letter in lower case: it's a o. We swap these characters, which leads to CodE!.

  3. We advance to the next character: this is the d that we've just moved here. We ignore it, because it has already been processed.

  4. We advance to the next character: this is the E that was moved here at step #1. We ignore it, because it has already been processed.

  5. We advance to the next character: this is a !. We ignore it, because it's not a letter.

Rules

  • You can assume that the input string is made exclusively of printable ASCII characters, in the range 32 - 126.

  • You may write either a full program or a function, which either prints or returns the result.

  • If the input string contains an odd number of letters, the last remaining letter can't be swapped with another one and should remain in place, no matter its case. The same logic applies if the string contains an even number of letters, but an odd number of uppercase letters and an odd number of lowercase letters.

  • This is code-golf, so the shortest answer in bytes wins. Standard loopholes are forbidden.

Test cases

Input : lLEhW OroLd!
Output: hELlO WorLd!

Input : rpGOZmaimgn uplRzse naC DEoO LdGf
Output: prOGRamming puzZles anD COdE GoLf

Input : eIt uqHKC RBWOO xNf ujPMO SzRE HTL EOvd yAg
Output: tHe quICK BROWN fOx juMPS OvER THE LAzy dOg

Input : NraWgCi: Nsas-eNEiTIsev rNsiTG!!
Output: WarNiNg: Case-sENsITive sTriNG!!

Not-so-random test cases:

Input : (^_^)
Output: (^_^)

Input : AWCTY HUOS RETP
Output: WATCH YOUR STEP

Input : hwn oeesd acsp nawyya
Output: who needs caps anyway

Input : SpMycaeIesKyBorekn
Output: MySpaceKeyIsBroken

Input : D's mroyr, Ivam. I'e faardi I act'n od htta.
Output: I'm sorry, Dave. I'm afraid I can't do that.
\$\endgroup\$
  • \$\begingroup\$ I assume a similar comment holds if the input contains an even number of letters, but an odd number of uppercase letters and an odd number of lowercase letters. \$\endgroup\$ – Greg Martin Feb 4 '17 at 20:14
  • 14
    \$\begingroup\$ This is a really clever challenge ... I also like the fact that test cases can be made by typing in a lowercase string, randomly changing some of the letters to uppercase, and then running the exact same program that solves the problem! \$\endgroup\$ – Greg Martin Feb 4 '17 at 20:15
  • 1
    \$\begingroup\$ @GregMartin I found out that the problem is its own inverse because when trying a test case I accidentally typed in the output instead of the input :-) \$\endgroup\$ – Luis Mendo Feb 4 '17 at 20:17
  • \$\begingroup\$ I think you should include test cases with more than one non-letter ASCII character ... I think some implementations could accidentally switch them with each other, when that's not supposed to happen. \$\endgroup\$ – Greg Martin Feb 4 '17 at 20:20
  • 3
    \$\begingroup\$ The tests cases should probably include a string without uppercase letters and a string without any letters at all. \$\endgroup\$ – Dennis Feb 5 '17 at 2:51

26 Answers 26

4
\$\begingroup\$

Jelly, 21 20 19 18 bytes

s2UF,
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.
    Ç         Call the helper link. 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.


s2UF,         Helper link. Argument: J (list of indices)

s2            Split J into pairs. If the length is odd, the last list will be
              a singleton list.
  U           Upend; reverse each pair. This is a no-op for singletons lists.
   F          Flatten, concatenating the pairs.
    ,          Pair the previous result with J.
\$\endgroup\$
18
\$\begingroup\$

Retina, 53 bytes

Not really clever, but a clean and quite readable solution

([a-z])(.*?)([a-z])
$3$2$1
([A-Z])(.*?)([A-Z])
$3$2$1

Try it online!

\$\endgroup\$
9
\$\begingroup\$

MATL, 22 bytes

2:"tttk<f2etAZ))P5M(Yo

Try it online! Or verify all test cases.

How it works

2:"       % Do the following twice
  ttt     %   Input string (implicit). Push three more copies
  k       %   Convert to lowercase
  <f      %   Indices of characters that had their code point increased by
          %   the lowercase conversion, i.e. that were uppercase letters
  2e      %   Convert to 2-row matrix. This pads a zero in the lower-right 
          %   corner if necessary
  tAZ)    %   Keep only columns that don't contain zeros. Thus if there
          %   was a character that can't be swapped it will be ignored             
  )       %   Get 2-row matrix of characters at those positions
  P       %   Flip vertically. This does the swapping
  5M      %   Push matrix of original indices again
  (       %   Write the swapped characters onto their original positions
  Yo      %   Change case. In the first iteration, this prepares the
          %   string so the second iteration will process the letters that
          %   were originally lowercase. In the second iteration, it
          %   undoes the change of case 
          % End (implicit)
          % Display (implicit)
\$\endgroup\$
6
\$\begingroup\$

Bash + Unix utilities, 77 62 57 56 54 bytes

sed -r "s/([$1)([^$1*)([$1)/\3\2\1/g"||$0 a-z]|$0 A-Z]

Input in stdin. Output in stdout.

(In this last version, stderr happens to be written to also, but PPCG consensus seems to be that that's OK -- stderr is simply ignored.)

Edit 1: Thanks to @Dennis for 15 bytes! Improvements: (a) Taking input via stdin; (b) combining 2 sed scripts into one; and (c) replacing tr with substitution via bash parameter expansion; (b) and (c) disappeared in Edit 2.

Edit 2: Shorter by 5 additional bytes. Used a function call to replace both (b) and (c) in Edit 1.

Edit 3: One more byte -- passed ] as part of the function arguments.

Edit 4: Replaced the two function calls with calls to the program itself when it has no arguments.

Testbed and sample output:

for x in 'lLEhW OroLd!' 'rpGOZmaimgn uplRzse naC DEoO LdGf' 'eIt uqHKC RBWOO xNf ujPMO SzRE HTL EOvd yAg' 'NraWgCi: Nsas-eNEiTIsev rNsiTG!!' '(^_^)' 'AWCTY HUOS RETP' 'hwn oeesd acsp nawyya' 'SpMycaeIesKyBorekn' "D's mroyr, Ivam. I'e faardi I act'n od htta."; do ./swapping <<<"$x" 2>/dev/null; done

hELlO WorLd!
prOGRamming puzZles anD COdE GoLf
tHe quICK BROWN fOx juMPS OvER THE LAzy dOg
WarNiNg: Case-sENsITive sTriNG!!
(^_^)
WATCH YOUR STEP
who needs caps anyway
MySpaceKeyIsBroken
I'm sorry, Dave. I'm afraid I can't do that.
\$\endgroup\$
6
\$\begingroup\$

ES6, 185 95 bytes

i=>(o=[...i]).map((c,j)=>/[a-z]/i.test(c)?o[e=c>"Z"]=1/(b=o[e])?o[o[j]=o[b],b]=c:j:0)&&o.join``

Solution severely shortened with the help of @Neil, @Arnauld and @edc65

Explanation

f = i =>
  // Get array of characters from input string
  (o = [...i])
    .map((c, j) => 
      // Check if it's a text character, otherwise skip it
      /[a-z]/i.test(c) ? 
        // Get last character position for case
        // merged with setting a variable for if the character is lowercase
        // merged with storing the current case character position,  
        // under properties on the array (with keys "true" or "false")
        o[e = c>"Z"] =
          // Check if there exists a character position to switch with
          // merged with storing the current position for quick access
          1/(b=o[e]) ? 
            // This statement will end up returning the Array subset, 
            // which will be falsy in the above conditional since (1/[])==false
            o[
              // Switch left character to the right
              o[j]=o[b]
            // Switch right character to the left
            ,b]=c : 
            // No character exists for case, so return current character position
            j
         // It was not a text character, so do nothing
         :0
      )
  // Join array and return as result
  && o.join``;

`lLEhW OroLd!
NraWgCi: Nsas-eNEiTIsev rNsiTG!!
rpGOZmaimgn uplRzse naC DEoO LdGf
eIt uqHKC RBWOO xNf ujPMO SzRE HTL EOvd yAg
(^_^)
AWCTY HUOS RETP
hwn oeesd acsp nawyya
SpMycaeIesKyBorekn
D's mroyr, Ivam. I'e faardi I act'n od htta`
  .split`\n`
  .map(testCase => console.log(f(testCase)));

\$\endgroup\$
  • \$\begingroup\$ 6 bytes, the enclosing parens are redundant when we remove the second statement :) Nice. \$\endgroup\$ – Jan Feb 5 '17 at 15:42
  • 2
    \$\begingroup\$ Please ignore my last comment. Here's the 99: /[a-z]/i.test(c)?o[e=c>"Z"]=1/(b=o[e])?[o[b],o[j]]=[c,o[b]]:j:0 \$\endgroup\$ – Arnauld Feb 5 '17 at 16:06
  • 2
    \$\begingroup\$ [o[b],o[j]]=[c,o[b]] could be o[o[j]=o[b],b]=c \$\endgroup\$ – edc65 Feb 5 '17 at 17:27
  • \$\begingroup\$ The real masterstroke here is using true and false as indexes for an array \$\endgroup\$ – edc65 Feb 5 '17 at 17:29
  • \$\begingroup\$ Thanks guys, down to 95 now. It starts becoming really hard to document the solution in a way that makes sense. XD @edc65 they're stored as properties on the array object, not indexes. Yeah Arnauld figured out that they were being stored on the character array, but the reuse of the object was more of a lucky accident I think which came from a separate suggestion. Initially it was stored on a separate object which of course was completely unnecessary for the scope of the challenge. \$\endgroup\$ – Jan Feb 5 '17 at 18:31
3
\$\begingroup\$

Python, 82 bytes

lambda s:S(r.lower(),t,S(r,t,s))
import re
S=re.sub
r='([A-Z])(.*?)'*2
t=r'\3\2\1'

Try it online!

\$\endgroup\$
  • \$\begingroup\$ how does it work? is the lambda even called? \$\endgroup\$ – Sarge Borsch Feb 5 '17 at 8:37
  • \$\begingroup\$ The lambda is the actual (function) submission. Everything else is just accompanying code that has to be executed before the lambda is called. \$\endgroup\$ – Dennis Feb 5 '17 at 8:39
3
\$\begingroup\$

QBasic, 229 bytes

LINE INPUT s$
FOR i=1TO LEN(s$)
c$=MID$(s$,i,1)
IF"@"<c$AND"[">c$THEN
IF u THEN MID$(s$,u,1)=c$:MID$(s$,i,1)=u$
u=-i*(u=0)
u$=c$
ELSEIF"`"<c$AND"{">c$THEN
IF l THEN MID$(s$,l,1)=c$:MID$(s$,i,1)=l$
l=-i*(l=0)
l$=c$
END IF
NEXT
?s$

Strategy

We loop through the input string. When we encounter an uppercase letter, we store it and its position. The second time we encounter an uppercase letter, we use those stored values to swap it with the previous one. Same for lowercase.

(I was about to post a rather longer version that used an array, because I thought that QBasic strings were immutable. Then I stumbled across the fact that MID$(strng$, index, length) = replacement$ works just fine. Live and learn.)

Ungolfed + commented

LINE INPUT text$

FOR i = 1 TO LEN(text$)
  char$ = MID$(text$, i, 1)
  IF "A" <= char$ AND "Z" >= char$ THEN
    ' Uppercase
    IF upperIndex = 0 THEN
      ' This is the first of a pair of uppercase letters
      ' Store the letter and its index for later
      upperLetter$ = char$
      upperIndex = i
    ELSE
      ' This is the second of a pair of uppercase letters
      ' Put it at the position of the previous uppercase letter
      ' and put that letter at this letter's position
      MID$(text$, upperIndex, 1) = char$
      MID$(text$, i, 1) = upperLetter$
      upperIndex = 0
    END IF
  ELSEIF "a" <= char$ AND "z" >= char$ THEN
    ' Lowercase
    IF lowerIndex = 0 THEN
      ' This is the first of a pair of lowercase letters
      ' Store the letter and its index for later
      lowerLetter$ = char$
      lowerIndex = i
    ELSE
      ' This is the second of a pair of lowercase letters
      ' Put it at the position of the previous lowercase letter
      ' and put that letter at this letter's position
      MID$(text$, lowerIndex, 1) = char$
      MID$(text$, i, 1) = lowerLetter$
      lowerIndex = 0
    END IF
  END IF
NEXT i

PRINT text$
\$\endgroup\$
2
\$\begingroup\$

C++11 (GCC), 154 149 bytes

#include<algorithm>
[](std::string s){int*p,u,l=u=-1;for(auto&c:s)(c|32)-97<26U?p=&(c&32?u:l),~*p?(std::swap(c,s[*p]),*p=-1):*p=&c-&s[0]:0;return s;}
\$\endgroup\$
  • 1
    \$\begingroup\$ You should either also #include<string> or switch to C++14 and declare a generic lambda [](auto s) and assume s to be of std::string. Also, declaring [](auto&s) saves you from returning the string as modifying input arguments to serve as output is allowed. \$\endgroup\$ – Karl Napf Feb 13 '17 at 12:28
2
\$\begingroup\$

Qbasic, 436 408 bytes

LINE INPUT a$:b=len(a$):FOR a=1TO b:t$=MID$(a$,a,1)
IF"@"<t$AND"[">t$THEN
b$=b$+"U":u$=u$+t$
ELSEIF"`"<t$AND"{">t$THEN
b$=b$+"L":l$=l$+t$
ELSE b$=b$+t$
END IF:NEXT
FOR x=1TO b STEP 2:g$=g$+MID$(u$,x+1,1)+MID$(u$,x,1):h$=h$+MID$(l$,x+1,1)+MID$(l$,x,1):NEXT
FOR x=1TO b:t$=MID$(b$,x,1)
IF"U"=t$THEN
u=u+1:z$=z$+MID$(g$,u,1)
ELSEIF"L"=t$THEN l=l+1:z$=z$+MID$(h$,l,1)
ELSE z$=z$+t$
END IF:NEXT:?z$

Saved one byte thanks to DLosc. Saved several more by changing handling of non-letter chars.

This basically consists of three parts:

  • Splitting the input into 3 strings (Uppercase, Lowercase, and a map (also holding the other chars))
  • Flipping the uppercase and lowercase letters
  • Using the map to (re)construct the output.

A more detailed explanation (note that this is of an earlier version of the code, but the principle still applies):

' --- Part I: Reading the input
LINE INPUT a$
'This FOR loop takes one character at a time
b=len(a$):FOR a=1TO b
' And checks in what category the character belongs
t$=MID$(a$,a,1):SELECT CASE t$
' For each group, char t$ is added to that group (u$ for uppercase, 
' l$ for lowercase. The map in b$ is updated with a U or L on this index,
' or with the non-letter char t$.
CASE"A"TO"Z":b$=b$+"U":u$=u$+t$
CASE"a"TO"z":b$=b$+"L":l$=l$+t$
CASE ELSE:b$=b$+t$
END SELECT:NEXT

' --- Part II: Swapping within case-groups
' Loop through u$ and l$ twp chars at a time, and add those chunks in reverse order
' to g$ and h$. Because mid$ doesn't fail past the end of a string (but returns ""), 
' this automatically compensates for odd-length groups.
FOR x=1TO b STEP 2:g$=g$+MID$(u$,x+1,1)+MID$(u$,x,1):h$=h$+MID$(l$,x+1,1)+MID$(l$,x,1):NEXT

' --- Part III: Read the map to put it all back together
FOR x=1TO b:t$=MID$(b$,x,1)
' See what group was in this spot, then read the next char from the flipped string.
' This keeps an index on those strings for the next lookup.
IF t$="U"THEN
u=u+1:z$=z$+MID$(g$,u,1)
ELSEIF t$="L"THEN l=l+1:z$=z$+MID$(h$,l,1)
' The map contains a non-letter char, just drop that in
ELSE z$=z$+t$
' And finally,display the end result.
END IF:NEXT:?z$
\$\endgroup\$
2
\$\begingroup\$

PHP, 108 93 83 bytes

<?=preg_replace([$a="/([a-z])([^a-z]*)([a-z])/",strtoupper($a)],"$3$2$1",$argv[1]);

Previous version (93 bytes)

<?=preg_replace(["/([a-z])([^a-z]*)([a-z])/","/([A-Z])([^A-Z]*)([A-Z])/"],"$3$2$1",$argv[1]);

Thanks to @user59178 for reminding me that preg_replace() can take arrays of strings as arguments.


The original answer (108 bytes)

$f=preg_replace;echo$f("/([a-z])([^a-z]*)([a-z])/",$r="$3$2$1",
$f("/([A-Z])([^A-Z]*)([A-Z])/",$r,$argv[1]));

The code is wrapped here to fit the available space.
It can be executed from the command line:

$ php -d error_reporting=0 -r '$f=preg_replace;echo$f("/([a-z])([^a-z]*)([a-z])/",$r="$3$2$1",$f("/([A-Z])([^A-Z]*)([A-Z])/",$r,$argv[1]));' 'lLEhW OroLd!'

A 1-byte shorter version is possible on PHP 7 by squeezing the assignment of $f inside its first call:

echo($f=preg_replace)("/([a-z])([^a-z]*)([a-z])/",$r="$3$2$1",
$f("/([A-Z])([^A-Z]*)([A-Z])/",$r,$argv[1]));

Both solutions, with test cases and ungolfed versions can be found on Github.

\$\endgroup\$
  • 1
    \$\begingroup\$ preg_replace can take an array of replacements to do so you only need one call. In addition it's shorter to use <?= than echo. With these it's simple to get your answer down to 93 bytes. \$\endgroup\$ – user59178 Feb 6 '17 at 18:18
  • \$\begingroup\$ You are right about preg_replace(). I forgot about it. I don't like <?= (in my opinion <? is not part of the language, it is just a marker) and I like to write short one-line programs that can be executed from the command line using php -r. But for the purpose of code golf you are right again. I can save 1 byte using <?=. \$\endgroup\$ – axiac Feb 6 '17 at 21:14
1
\$\begingroup\$

Mathematica, 96 bytes

s[#,r="([a-z])(.*?)([a-z])"]~(s=StringReplace[#,RegularExpression@#2->"$3$2$1"]&)~ToUpperCase@r&

A port of Leo's Retina answer, which uses regular expressions.

\$\endgroup\$
  • \$\begingroup\$ I'm honestly surprised that mathematica doesn't have a builtin for that, I mean, if "When's Easter sunday", "When's the sunset" and "what is the shape of france" get builtins, this one should too! \$\endgroup\$ – sagiksp Feb 11 '17 at 15:18
1
\$\begingroup\$

Python 2, 124 bytes

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

Not as short as my regex-based solution, but I think it's still interesting.

Try it online!

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

Bean, 83 bytes

Hexdump:

00000000 26 53 d0 80 d3 d0 80 a0 5d 20 80 0a a1 81 81 00  &SÐ.ÓÐ. ] ..¡...
00000010 23 81 01 20 80 0a a1 81 81 02 23 81 01 a8 db c1  #.. ..¡...#..¨ÛÁ
00000020 ad da dd a9 a8 db de c1 ad da dd aa bf a9 a8 db  .ÚÝ©¨ÛÞÁ.Úݪ¿©¨Û
00000030 c1 ad da dd 29 a4 b3 a4 b2 a4 31 a8 db e1 ad fa  Á.ÚÝ)¤³¤²¤1¨Ûá.ú
00000040 dd a9 a8 db de e1 ad fa dd aa bf a9 a8 db e1 ad  Ý©¨ÛÞá.úݪ¿©¨Ûá.
00000050 fa dd 29                                         úÝ)
00000053

Equivalent JavaScript:

a.replace(/([A-Z])([^A-Z]*?)([A-Z])/g,'$3$2$1').replace(/([a-z])([^a-z]*?)([a-z])/g,'$3$2$1')

Explanation:

Implicitly taking the first line of input unformatted as a (since newlines can't be part of scrambled string), and implicitly outputs unscrambled string by sequentially replacing uppercase, then lowercase pairs.

Try demo here.

Try test suite here.

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

Ruby, 81 bytes

puts f=->(i,s){i.gsub /([#{s})([^#{s}*)([#{s})/,'\3\2\1'}[f[$*[0],'a-z]'],'A-Z]']
\$\endgroup\$
1
\$\begingroup\$

JavaScript (ES6), 80 bytes

Based on Leo's Retina answer.

s=>eval("s"+(r=".replace(/([A-Z])([^A-Z]*)([A-Z])/g,'$3$2$1')")+r.toLowerCase())

This works because the only uppercase characters in the code .replace(/([A-Z])([^A-Z]*)([A-Z])/g,'$3$2$1') are A and Z, which are used to describe the character ranges. This is precisely what we need to transform into lowercase in order to process the second pass.

Test cases

let f =

s=>eval("s"+(r=".replace(/([A-Z])([^A-Z]*)([A-Z])/g,'$3$2$1')")+r.toLowerCase())

console.log(f("lLEhW OroLd!"));
console.log(f("rpGOZmaimgn uplRzse naC DEoO LdGf"));
console.log(f("eIt uqHKC RBWOO xNf ujPMO SzRE HTL EOvd yAg"));
console.log(f("NraWgCi: Nsas-eNEiTIsev rNsiTG!!"));
console.log(f("(^_^)"));
console.log(f("AWCTY HUOS RETP"));
console.log(f("hwn oeesd acsp nawyya"));
console.log(f("SpMycaeIesKyBorekn"));
console.log(f("D's mroyr, Ivam. I'e faardi I act'n od htta."));

\$\endgroup\$
  • \$\begingroup\$ Actually, it turns out to be very similar to this Python answer by Dennis. \$\endgroup\$ – Arnauld Feb 5 '17 at 19:34
1
\$\begingroup\$

ES6 155 - 195 bytes

I know there's already a better answer, but I wanted to try without regex. This one works on punctuation too, but that seems to violate the (^_^) test. In that case I have another c() function, given below.

f=(s)=>{d={};s=[...s];for(i in s){b=s[i];for(j in s)if(i<j&!d[i]&c(s[j])==c(b)){d[j]=1;s[i]=s[j];s[j]=b;break}}return s.join('')}
c=c=>~(c.charCodeAt()/32)

f("M I'o DaG") //> I'M a GoD
f("(^_^)")     //> )_^^(

c=c=>((c!=c.toUpperCase())<<1|c!=c.toLowerCase())||c.charCodeAt()

f("M I'o DaG") //> I'M a GoD
f("(^_^)")     //> (^_^)

Explanation

f=(s)=>{
    d={};        //list of indexes already swapped
    s=[...s];        //string to array, stolen from above ES6 answer
    for(i in s){
        b=s[i];        //keep a note of what we are swapping
        for(j in s)        //iterate over the array again
            if( i<j & !d[i] & c(s[j])==c(b) ){
                        //only pay attention after we pass i'th
                        //only swap if this char hasn't been swapped
                        //only swap if both chars in same 'category'
                d[j]=1;        //note that the latter char has been swapped
                s[i]=s[j];
                s[j]=b;
                break        //avoid swapping on the same 'i' twice
            }
    }
    return s.join('')        //return as string
}
\$\endgroup\$
1
\$\begingroup\$

Perl 6, 56 bytes

{for "A".."Z","a".."z" ->@c {s:g/(@c)(.*?)(@c)/$2$1$0/}}

Takes a string variable as an argument, and modifies it in-place so that after calling the lambda the variable contains the result.

Longer than it would be in Perl, because:

  • The new regex syntax is more verbose, e.g. writing out the character classes would look like <[A..Z]> instead of [A-Z].
  • Regexes are first-class source code parsed at compile time, and a string can only be interpolated into them at run-time if it consists of a self-contained subregex (i.e. you can't interpolate a string into a character class).
  • Explict EVAL, which would allow more flexibility, requires the golf-unfriendly use MONKEY-SEE-NO-EVAL; pragma.

On the plus side, an array in a @ variable can be referenced directly in a regex, and is treated as an alternation.


Perl 6, 65 bytes

{reduce ->$_,@c {S:g/(@c)(.*?)(@c)/$2$1$0/},$_,"A".."Z","a".."z"}

Functional version (outputs the result as the return value of the lambda).

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

R, 343 Bytes

Terribly clumsy R solution:

f <- function(x) {
        y=unlist(strsplit(x,""))
        z=data.frame(l=ifelse(y %in% letters,0,ifelse(y %in% LETTERS,1,2)),s=y)
        l <- list(which(z$l==0),which(z$l==1))
        v <- unlist(l)
        for(j in 1:2) for (i in seq(1,ifelse(length(l[[j]])%%2==1,length(l[[j]])-2,length(l[[j]])-1),2)) l[[j]][i:(i+1)] <- rev(l[[j]][i:(i+1)])
        z[v,] <- z[unlist(l),]
        return(z$s)
    }

f("D's mroyr, Ivam. I'e faardi I act'n od htta.")

# [1] I ' m   s o r r y ,   D a v e .   I ' m   a f r a i d   I   c a n ' t   d o   t h a t .
\$\endgroup\$
1
\$\begingroup\$

Python 2, 181 bytes

Much longer than it should be but anyway:

def F(s):
 for l in[i for i,c in enumerate(s)if c.isupper()],[i for i,c in enumerate(s)if c.islower()]:
  for a,b in zip(l[0::2],l[1::2]):s=s[:a]+s[b]+s[a+1:b]+s[a]+s[b+1:]
 print s

This first creates two lists: one of the indices of the uppercase characters and one for the lowercase characters. Each of these lists is looped through in pairs of indices, and the characters at those indices are switched.

I'll golf this down tomorrow, but for now it's time to sleep.

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

Pip, 28 bytes

Y[XLXU]aRy.`.*?`.y{Sa@0a@va}

Takes input as a command-line argument. Try it online!

Explanation

This is a regex solution, using the builtin regex variables XL (lowercase letters, `[a-z]`) and XU (uppercase letters, `[A-Z]`).

                              a is 1st cmdline arg; v is -1 (implicit)
Y[XLXU]                       Yank a list containing XL and XU into y
         y.`.*?`.y            Concatenate y, `.*?`, and y itemwise, giving this list:
                              [`[a-z].*?[a-z]`; `[A-Z].*?[A-Z]`]
       aR                     In a, replace matches of each regex in that list...
                  {        }  ... using this callback function:
                   Sa@0a@v     Swap the 0th and -1st characters of the match
                          a    and return the resulting string
                              Print (implicit)

When the second argument to R is a list, the replacements are performed in series; thus, the lowercase replacement and the uppercase replacement don't interfere with each other.

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

Perl 5, 48 + 1 (-p) = 49 bytes

for$p(qw/([a-z]) ([A-Z])/){s/$p(.*?)$p/$3$2$1/g}

Try it online!

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

AWK, 121 129 bytes

BEGIN{FS=OFS=""}{for(a=1;a<=NF;a++){if($a~/[A-Z]/?U>0?p=U+(U=0):0*(U=a):$a~/[a-z]/?L>0?p=L+(L=0):0*(L=a):0>0){t=$a;$a=$p;$p=t}}}1

Try it online! Note: Link has 8 extra bytes to allow multiline input

Usage is fairly typical, but does require a version of AWK that accepts an empty string as the field separator (most versions of gawk but I'm pretty sure the original AWK would fail :( )

It's very straightforward as it simply iterates over each character and checks if it's found one of that case before. If so, it swaps the characters and resets the checked index. On the learning side of things, I'd never used an assignment statement within an assignment statement in AWK before. For some reason it had never come up. :)

I might be able to shave a couple of bytes by saying to assign OFS and FS outside a BEGIN block via command-line assignment or similar, but it's "cleaner" this way.

Adding the TIO link showed me that I had a transcription error that required 8 bytes to fix :( (I left out 0*(U=a):)

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

C (gcc), 212 206 bytes

#define I(a,b)if(S[j=i]>=a&S[i]<-~b){for(;S[++j]<a|S[j]>b;);j<l?s[i]=S[j],s[j]=S[i]:0;}
i,j,l;f(char*S){char*s=calloc(l=-~strlen(S),1);for(i=~0;++i<strlen(S);)if(!s[i]){s[i]=S[i];I(65,90)I(97,'z')}puts(s);}

Try it online!

\$\endgroup\$
  • \$\begingroup\$ @ceilingcat Thank you. \$\endgroup\$ – Jonathan Frech Aug 6 at 20:05
1
\$\begingroup\$

Stax, 18 bytes

âß:}\]ó☺æ■jφ╛jz/Φi

Run and debug it

The general approach is regex-based.

  • Two times do:
  • Find all matches for [a-z].*?[a-z].
  • Swap the first and last character in matches.
  • Invert case.
\$\endgroup\$
1
\$\begingroup\$

R, 223 163 bytes 148 bytes

EDIT: -60 bytes by implementing a for loop

EDIT: -15 bytes from Giuseppe

u=utf8ToInt(scan(,''));for(i in c(65,97)){l=which(u%in%i:(i+25));x=sum(l|1)%/%2;u[l[1:(x*2)]]=u[c(matrix(l,2)[2:1,1:x])]};cat(intToUtf8(u,T),sep="")

Try it online!

Works by testing if character is a lowercase or uppercase, places them in a matrix, inverts the matrix to extract the values in a swapped format. Then output with cat. Try it online struggles with scan(,'') if the code is more than one line, hence the semicolons throughout the single line of code.

\$\endgroup\$
  • \$\begingroup\$ I get 168 on your link, but this golf is 163 \$\endgroup\$ – Giuseppe Aug 7 at 21:28
  • \$\begingroup\$ And this brings it to 162. \$\endgroup\$ – Giuseppe Aug 7 at 21:30
  • \$\begingroup\$ this probably works; the x fiddling is the clever bit, but getting rid of m=matrix was 4 bytes as well. \$\endgroup\$ – Giuseppe Aug 7 at 21:37
  • \$\begingroup\$ What about the scan(,'') problem? And reducing the "lLEhW OroLd!" in TIO to scan(,'') or some other way to get input? \$\endgroup\$ – Sumner18 Aug 7 at 21:39
  • 1
    \$\begingroup\$ You missed a couple: tio.run/##Dc5NC4IwGADgv2LC8H1rEA76muzYIRC6GB3Eg6zEwdxibjSofvvy/… \$\endgroup\$ – Giuseppe Aug 7 at 22:17
0
\$\begingroup\$

Java 7, 117 bytes

String c(String s){String x="([a-z])(.*?)([a-z])",y="$3$2$1";return s.replaceAll(x,y).replaceAll(x.toUpperCase(),y);}

EDIT: Just noticed I have a similar answer as @Leo's Retina answer, even though I've thought about it independently..

Ungolfed:

String c(final String s) {
  String x = "([a-z])(.*?)([a-z])",
         y = "$3$2$1";
  return s.replaceAll(x, y).replaceAll(x.toUpperCase(), y);
}

Test code:

Try it here.

class M{
  static String c(String s){String x="([a-z])(.*?)([a-z])",y="$3$2$1";return s.replaceAll(x,y).replaceAll(x.toUpperCase(),y);}

  public static void main(String[] a){
    System.out.println(c("lLEhW OroLd!"));
    System.out.println(c("rpGOZmaimgn uplRzse naC DEoO LdGf"));
    System.out.println(c("eIt uqHKC RBWOO xNf ujPMO SzRE HTL EOvd yAg"));
    System.out.println(c("NraWgCi: Nsas-eNEiTIsev rNsiTG!!"));
    System.out.println(c("(^_^)"));
    System.out.println(c("AWCTY HUOS RETP"));
    System.out.println(c("hwn oeesd acsp nawyya"));
    System.out.println(c("SpMycaeIesKyBorekn"));
    System.out.println(c("D's mroyr, Ivam. I'e faardi I act'n od htta."));
  }
}

Output:

hELlO WorLd!
prOGRamming puzZles anD COdE GoLf
tHe quICK BROWN fOx juMPS OvER THE LAzy dOg
WarNiNg: Case-sENsITive sTriNG!!
(^_^)
WATCH YOUR STEP
who needs caps anyway
MySpaceKeyIsBroken
I'm sorry, Dave. I'm afraid I can't do that.
\$\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.