An ogl-edocf challenge

Input

A non-empty shuffled string consisting of ASCII characters in the range $[32..126]$.

Output

The output is obtained by applying successive rotations to the input string.

For each letter ([a-zA-Z]) in the input string, going from left to right:

• if the letter is in upper case, rotate all characters before it by one position to the left
• if the letter is in lower case, rotate all characters before it by one position to the right

Example

• The first letter is a "C". We should do a rotation to the left, but there's no character before this "C". So, there's nothing to rotate.
• The next letter is a "b". We rotate "C" to the right. Because it's a single character, it is left unchanged.
• The character "-" does not trigger any rotation, as it's not a letter.
• The next letter is a "A". We rotate "Cb-" to the left, which gives "b-CAd"
• The fourth and last letter is a "d". We rotate "b-CA" to the right, which gives "Ab-Cd"

Therefore, the expected output is "Ab-Cd".

Rules

• You may take input as a string or as an array of characters -- which may or may not be the same thing, depending on your language.
• You may also output an array of characters instead of a string.
• This is

Test cases

"cbad" -> "abcd"
"ACBD" -> "ABCD"
"caeBDF" -> "aBcDeF"
"aEcbDF" -> "abcDEF"
"ogl-edocf" -> "code-golf"
"W o,ollelrHd!" -> "Hello, World!"
"ti HIs SSta ET!" -> "tHis IS a tEST!"


Pyth, 21 20 bytes

VQ=k+.>k-}NG}Nr1GN)k


Try it here

Explanation

VQ=k+.>k-}NG}Nr1GN)k
VQ                )      For each N in the input...
.>k                 ... rotate k (initially '')...
-}NG}Nr1G        ... by (N is lowercase) - (N is uppercase)...
+            N       ... then append N...
=k                     ... and update k.
k     Output the result.

• You can use .U to reduce the input from 2nd value. This lets you drop =k from the start, and )k from the end as both the input and printing are implicit. Full program: .U+.>b-}ZG}Zr1GZ - link – Sok Sep 25 '18 at 7:54

Python 2, 10098 95 bytes

f=lambda s,p='':s and f(s[1:],[p[x:]+p[:x]+s[0]for x in[s[0].isupper()-s[0].islower()]][0])or p


Try it online!

Jelly, 14 bytes

ØẠŒHċ€ḅ-N⁸ṙ;ð/


A monadic link accepting a list of characters which yields a list of characters.

Try it online! Or see the test-suite.

How?

ØẠŒHċ€ḅ-N⁸ṙ;ð/ - Link - list of characters
/ - reduce by:
ð  -   a dyadic chain:  1st call is f(L=1stCharacter, R=2ndCharacter)
-                    ...then calls are f(L=previousResult, R=nextCharacter)
ØẠ             -     alphabet characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
ŒH           -     split in half = ["ABCDEFGHIJKLMNOPQRSTUVWXYZ","abcdefghijklmnopqrstuvwxyz"]
€         -     for each:
ċ          -       count occurrences (of R)
-                          e.g.: 'W' -> [1,0]; 'c' -> [0,1]; '@' -> [0,0]
ḅ-       -     convert from base -1             -1             1             0
N      -     negate                            1            -1             0
⁸     -     chain's left argument (i.e. L)
ṙ    -     rotate left by (the negate result)
;   -     concatenate R

• I don't know Jelly too well, but shouldn't I do the same as ḅ- in this case? It seems to work here, but not in your code. I'm a bit confused why. Also, is there a command to push the entire lists as separated items to the stack in Jelly (wait, Jelly isn't a stack-based language, is it..)? In that case a simple subtract can be used and you also won't need the negate if I'm not mistaken (similar as the last edit in my 05AB1E answer). – Kevin Cruijssen Sep 19 '18 at 21:05
• I yields a list - add ŒṘ to see a full representation. So ØẠŒHċ€IṪN⁸ṙ;ð/ would work. – Jonathan Allan Sep 19 '18 at 21:08
• Ah ok, that makes sense. Thanks for the explanation. Nice answer btw, already upvoted it yesterday. :) – Kevin Cruijssen Sep 20 '18 at 7:19

05AB1E, 181716 14 bytes

õsvy.uy.l-._y«


Explanation:

õ            # Start with an empty string
sv          # Loop over the characters y of the input
y.u       #  Check if y is an uppercase letter (1 if truthy; 0 if falsey)
y.l       #  Check if y is a lowercase letter (1 if truthy; 0 if falsey)
-      #  Subtract them from each other
._    #  Rotate the string that many times (-1, 0, or 1) toward the left
y«        #  Append the current character y to the string
# (And implicitly output the string after the loop)


K4, 43 33 bytes

Solution:

""{.q.rotate[-/y in'.QAa;x],y}/


Examples:

q)k)""{.q.rotate[-/y in'.QAa;x],y}/"Cb-Ad"
"Ab-Cd"
q)k)""{.q.rotate[-/y in'.QAa;x],y}/"ogl-edocf"
"code-golf"
q)k)""{.q.rotate[-/y in'.QAa;x],y}/"ti HIs SSta ET!"
"tHis IS a tEST!"


Explanation:

Iterate over the input string, rotating the previous output by 1, -1 or 0 depending upon it's position in the list "a-zA-Z".

""{.q.rotate[-/y in'.QAa;x],y}/ / the solution
""{                            }/ / iterate (/) over, starting x as ""
,y   / append y to
.q.rotate[             ;x]     / rotate x by ...
.QAa        / the lists "a..z" and "A..Z"
y in'              / y in each (') alphabet?
-/                   / subtract (-) over (/)


Notes:

• -10 bytes with inspiration from the 05AB1E solution

><>, 45 43 bytes

ii:2+7$.::&"{"@)@(*?}"@["&::&@)@(*?{&! ror  Try it online! The fact that ><> has stack rotation helps, but having to check the case of the letter doesn't. Explanation: i Get first inputted character i Get second. This is to prevent errors from rotating an empty stack :2+7$.      Jump to the second line if that was EOF
::&   Create copies of the input and store one in the register
"{"@)@(*     Check if the char is lower case
?}   If so rotate the stack
"@["&::&@)@(*?{   Rotate the other way if uppercase
&  Push the new char
! Skip the first i instruction
Skip to the second line on EOF
ro      Reverse the stack and output
r r     Cancel out the first reverse
o      Output the rest of the stack


Haskell, 101 91 bytes

-10 bytes inspired by Curtis Bechtel's answer (use '@'<c,c<'[' over elem c['A'..'Z'] and the according range for lower-cased letters).

g(x:y)=foldl((<>pure).(!))[x]y
x@(a:b)!c|''<c,c<'{'=last x:init x|'@'<c,c<'['=b++[a]|0<1=x


Try it online!

Explanation / Ungolfed

The operator (!) takes a non-empty string x on which we can pattern-match and a character:

x@(a:b) ! c
| '' < c, c < '{' = last x : init x  -- rotate x to the right by one
| '@' < c, c < '[' = b ++ [a]         -- rotate x to the left by one
| otherwise = x                       -- keep x as is


Now we can reduce the input's tail from the left to the right, starting with the first character of the input using:

\b a -> b!a ++ [a]


Haskell, 122 92 bytes

Thanks to BWO for the suggestions! I also saved a lot by taking a slightly different approach than my original answer.

l@(a:b)!c|''<c,c<'{'=last l:init l++[c]|'@'<c,c<'['=b++[a,c]|0<1=l++[c]
f(c:s)=foldl(!)[c]s


Try it online!

• You can swap the pattern-match of (#), use , over &&, use [l!!0,c] over head l:[c], 1>0 instead of True, you don't need to count f= and you can assume non-empty input which saves the l==[] guard - saving you 13 bytes: Try it online! – ბიმო Sep 19 '18 at 19:44
• Btw. I used the isLower and isUpper golf in my submission, I hope you're okay with that otherwise I'll reverse my edit. – ბიმო Sep 19 '18 at 19:53
• @BWO Thanks for the suggestions, and go right ahead! – Curtis Bechtel Sep 19 '18 at 21:07

JavaScript (Node.js), 116 102 bytes

f=(s,i=1)=>s[i]?f(s.replace(RegExp(^(.)(.{${i}})(?=[A-Z])|^(.{${i}})(.)(?=[a-z])),"$4$3$2$1"),i+1):s


Try it online!

Original (116111 106B)

s=>Buffer(s).map((x,i)=>s=(t=s[S="slice"](i),i<2)?s:x>64&x<91?s[S](1,i)+s[0]+t:x>96&x<123?s[i-1]+s[S](0,i-1)+t:s)&&s

s=>Buffer(s).map((x,i)=>i<2|--x%32>25|x<64?s:s=[s[S="slice"](1,i)+s[0],s[i-1]+s[S](0,i-1)][+(x>95)]+s[S](i))&&s

s=>Buffer(s).map((x,i)=>!i|--x%32>25|x<64?s:s=(w=x>95,t=s.slice(1-w,i-w),w?s[i-1]+t:t+s[0])+s.slice(i))&&s

• It’s probably shorter to do eval(regex) than using the constructor – Downgoat Sep 19 '18 at 15:52
• @Downgoat I'm afraid that's not the case because the slashes are needed in the case of eval(regex), so -2+2=0 and thus it doesn't help reducing byte counts. – Shieru Asakoto Sep 19 '18 at 15:56
• @Downgoat It is worth using eval() when at least one flag is used: eval('/./g') is 3 bytes shorter than RegExp('.','g'). – Arnauld Sep 20 '18 at 8:45
• @Arnauld That's true, but I don't use flags here. – Shieru Asakoto Sep 20 '18 at 10:01
• @ShieruAsakoto (Sure. My comment was primarily addressed to Downgoat to explain why it's not worth doing it here.) – Arnauld Sep 20 '18 at 10:09

Ruby, 51 bytes

->s{w=[];s.map{|x|w.rotate!(x=~/\W/||?_<=>x)<<x};w}


Try it online!

Input and output are arrays of characters

The trick:

The code is pretty straightforward, except perhaps the rotation part:

(x=~/\W/||?_<=>x)


x is a single character, which could be a letter, the first expression x=~/\W/ returns nil if it's a letter, and 0 otherwise. If it's 0, we're done, if not, the logical or checks the second expression: ?_<=>x returns -1 for upper case and 1 for lower case. So the rotation is:

• -1 (1 to the left) for upper case
• +1 (1 to the right) for lower case
• 0 (no rotation) if it's not a letter

Red, 110 bytes

func[s][p: s forall s[if all[(a: first s)>#"@"a < #"["][move p back s]if all[a >#""a <#"{"][move back s p]]p]


Try it online!

Explanation:

f: func [ s ] [
p: s                                ; store the starting position of the string in p
forall s [                          ; iterate through the entire string
a: first s                      ; store the current character in a
if all [ a > #"@" a < #"[" ] [  ; if a is a uppercase letter
move p back s               ; move the starting char to the position before current
]
if all [ a > #"" a < #"{" ] [  ; if a is a lowercase letter
move back s p               ; move the character before the current one to the start
]
]
p                                   ; return the string
]


Perl 6, 47 bytes

*.reduce:{|@$^a.rotate($^b~~/\w/&&'_'leg$b),$b}


Try it online!

Works on an array of chars.

• Ah, I would have gone for using cmp and uc/lc, but that comes to 49 bytes – Jo King Sep 25 '18 at 1:18
• Darn, 48 bytes Almost caught up – Jo King Sep 25 '18 at 1:38

Japt, 1716 14 bytes

Takes input as an array of characters, outputs a string

;rÏiXéCøY -BøY


Try it

Explanation

 rÏ                :Reduce by passing the current result (X) & the current element (Y) through a function
i               :  Prepend to Y
Xé             :  X rotated right by
;     B            :    Uppercase alphabet
øY          :    Contains Y?
-        :    Subtract
;          C       :    Lowercase alphabet
øY     :    Contains Y?


Jelly, 19 bytes

W€ḷṙ01ŒlƑ?-ŒuƑ?};ʋ/


Try it online!

Java 10, 149 119 bytes

s->{for(int i=1;i<s.length();)s=s.replaceAll("^(.)(.{"+i+"})(?=[A-Z])|^(.{"+i+++"})(.)(?=[a-z])","$4$3$2$1");return s;}


Port of @ShieruAsakoto JavaScript answer, so make sure to upvote him.

Try it online.

Explanation:

s->{                          // Method with String as both parameter and return-type
for(int i=1;i<s.length();)  //  Loop i in the range [1, length)
s=s.replaceAll("^(.)(.{"+i+"})(?=[A-Z])|^(.{"+i+++"})(.)(?=[a-z])","$4$3$2$1");
//   Rotate the substring of [0, i] either left or right
return s;}                  //  Return the modified input-String as result


Stax, 32 bytes

éG7[Æ┐äZ▬Θε♫∙~╞ÉH╔u╬←J╛ü╢(┼▒uX)Ü


Run and debug it

B]s{c97 123:bsaa|)sc65 91:bsaa|(s+c}fd  #Full program, unpacked, implicit input
B]s                                     #Unons-left, singleton, swap top 2 of stack
{c                                   #Copy iterative letter
97 123                             #Put 97 and 123 on stack(lower case)
:bsaa                        #Check if iterative letter is between range, swap orientation back to proper order
|)                      #Rotate if the letter is within range
sc                    #Swap top 2 and copy top
65 91               #Put 65 and 91 on stack (Capitals)
:bsaa          #Check if iterative letter is between range, swap orientation back to proper order
|(        #Rotate if the letter is within range
s+c     #swap, concat and copy
}fd  #remove duplicate of original answer after loop and implicitly print


A lot of stack swapping which is probably unnecessary. I really would like to get this down more, but I was strugging with the ordering of the stack. Maybe somebody can figure it out if they are bored. Will keep working on it.

Attache, 69 bytes

~Fold[{s'r.=_'sp;Rotate[s,&-!Has&r[0]=>ALPHA'alpha]+r}@SplitAt]#Iota


Try it online!

Explanation

General shape

The function looks generally like this:

~Fold[{...}]#Iota


Which folds {...} over each member in the range from 0 to #input - 1 (Iota), starting with the input as a seed.

The inner function

The following function is called as f[building, index] and is called with each index from 0 to #input exclusive. @SplitAt calls SplitAt on these arguments, splitting the input string on index.

{s'r.=_'sp;Rotate[s,&-!Has&r[0]=>ALPHA'alpha]+r}
{                                               }    anonymous function taking the split string
_'sp                                           concat the input with a space
e.g.: ["cd-", "Adf!", " "]
(this is to handle index = 0)
s'r.=                                               s is the first member, and r is the second
Rotate[s,                         ]       rotate s by:
ALPHA'alpha          over uppercase and lowercase alphabets:
Has&r[0]=>                       test if r[0] is in each alphabet
e.g.: [true, false]
&-!                               subtract the second from the first
e.g.: (true - false) = 1 - 0 = 1
s is rotated according to the following map:
uppercase =>  1
symbol    =>  0
lowercase => -1
+r     append the right portion of the string


Essentially, this function rotates the left portion of the string according to the first character of the right portion.

Charcoal, 20 bytes

ＦＳ≔⁺⭆ω§ω⁻⁺λ№αι№βιιωω


Try it online! Link is to verbose version of code. Explanation:

ＦＳ


Loop over the input characters.

≔⁺⭆ω§ω⁻⁺λ№αι№βιιω


Map over the string of collected characters so far, cyclically indexing into the collected characters so far with the index incremented or decremented if the current character is upper or lower case respectively. This completes the rotation. The next character is then concatenated, and the result assigned back to the string.

ω


Print the result.

R, 107102 100 bytes

Massive because R's string manip is bulky. Can anyone get it under 100?

-5 bytes using the "set loop variables to F to avoid initializing" trick.

-2 bytes by assuming all characters are printable and using 2*!k%%97>25 rather than 2*k%in%97:122 for testing lower case, using operator precedence.

function(x){for(k in u<-utf8ToInt(x))u[1:F-1]=u[(1:(F=F+1)+k%in%65:90-2*!k%%97>25)%%F];intToUtf8(u)}


Try it online!

• Come join us (it's just me right now...) in the R golf chatroom to bounce some ideas around! My guess is that the permutation piece is as short as it can be with this approach but not having tried it myself, I can't say for sure. – Giuseppe Sep 19 '18 at 16:39

Japt, 25 23 bytes

I give up, Can't make it shorter

-2 bytes from @ETHproductions

£=¯Y éXè\a -Xè\A)+UsYÃU


£=¯Y éXè\a -Xè\A)+UsYÃU     Full program. Implicit input U
£                           map each char
=                          set U equal to:
¯Y                        U sliced to current mapped value
éXè\a -Xè\A)            and rotated left or right 1 char
+UsY        append the non-sliced U value
ÃU    Output U


Try it online!

• Sadly, I can't find a shorter way to do éXè\a -Xè\A either :-( You could save two bytes by changing the double space to a ), and removing © (implicit comma means that U is still outputted) – ETHproductions Sep 20 '18 at 4:44
• Save another 2 bytes by dropping ÃU and using the -h flag. – Shaggy Sep 20 '18 at 11:44

Retina, 6764 58 bytes

^
¶
+(.*)(.)¶([a-z])|(.)(.*)¶([A-Z])|¶(.)
$2$1$3$5$4$6$7¶  -9 bytes thanks to @Neil removing the three unnecessary ? I had added, as well as the unnecessary (.*) in the else-case. Try it online or verify all test cases. (NOTE: Outputs with a trailing newline. The header in the test suite is to test each input-line as a separate test case, and the footer is to remove that trailing newline for a more compact output.) Explanation: Prepend a newline before the input: ^ ¶  Continue replacing as long as we can find a match: +  Everything else is three different checks merged together: If the character right after the newline is lowercase letter: rotate everything before the newline once towards the right, and then append that character and the newline: (.*)(.)¶([a-z])$2$1$3¶


If the character right after the newline is an uppercase letter: rotate everything before the newline once towards the left, and then append that character and the newline:

(.)(.*)¶([A-Z])
$2$1$3¶  Else (neither a lowercase nor uppercase letter): simply shift the newline once towards the right for the next 'iteration': ¶(.)$1¶


These three checks above are merged with regex OR statements (|) and larger group-replacements to make it act like an if(lowercase) ... elseif(uppercase) ... else ...:

• I don't think you need the ?s - if there's nothing to rotate yet, it doesn't matter whether there's a letter. – Neil Sep 20 '18 at 13:11
• Also, replacing (.*)¶(.) with $1$2¶ can be simplified to replacing ¶(.) with \$1¶ as the other capture doesn't affect the result. – Neil Sep 20 '18 at 13:15
• @Neil Ah, of course thanks. -9 bytes right there! :) – Kevin Cruijssen Sep 20 '18 at 13:18

Pyth, 16 bytes

em=+.>k-F}RGrBd2


Try it here!

Explanation:

em=+.>k-F}RGrBd2dQ    Autofill variables
m               Q    for each d in Q (the input):
rBd2       Take [d, swapcase(d)]
}RG           Figure out which are in the lowercase alphabet (returns [1,0] for lowercase, [0,1] for uppercase, [0,0] for non-letters)
-F              Fold on subtraction (1 for lowercase, -1 for uppercase, 0 for non-letter)
.>k                Cycle the processed part (k) of the input right by ^ steps
+            d      Add in the new character at the end
=   k                Store new process step back into k (k starts as an empty string)
e                     Get the (e)nd step's output.


MATL, 20 bytes

ttYo-ZS"X@q:&)w@YSwh


Try it online!

-4 bytes thanks to Luis Mendo.

Converts uppercase/lowercase/non-letter to [-1,0,1] (first half of the program). Applies circshift consecutively (second half). I'm wrecking my brain if there's a better way to map uppercase/lowercase to [-1,0,1] (see the second version), and perhaps a way to reverse the string right away so as to get rid of the two w's needed for the &).

C (clang), 168159153 119 bytes

g,i,j;f(char*a){for(i=0;a[j=i++];islower(a[i])?g=a[j],bcopy(a,a+1,j),*a=g:isupper(a[i])?g=*a,bcopy(a+1,a,j),a[j]=g:0);}


-26 thanks to @ceilingcat

Try it online!

• Suggest g=a[j=i++];islower(a[i])?bcopy(a,a+1,j) instead of a[j=i++];islower(a[i])?g=a[j],bcopy(a,a+1,j) – ceilingcat Oct 30 '18 at 0:59