Mathematica, 173 169 155 bytes
f=0>1;t=!f;c=Characters;u=ToUpperCase;StringJoin/@MapThread[#@#2&,{Reverse[{LetterQ@#,#==(u@#)}&/@c@#/.{{f,_}->(#&),{t,t}->u,{t,f}->ToLowerCase}&/@#],c/@#},2]&
This is a function taking an array of two strings, e.g. {"Foo","bAR"}
and outputting an array of two strings. Un-spatially-compressing it, rewriting the scheme f@x
as f[x]
wherever it appears, expanding the notation abbreviations (f=0>1
a.k.a. False
,t=!f
a.k.a. True
, c=Characters
, and u=ToUpperCaseQ
), and un-replacing UpperCaseQ[#] with #==u@#
(this character equals its uppercased version), it is:
StringJoin /@ MapThread[#[#2] &, {
Reverse[
{ LetterQ[#], UpperCaseQ[#] } & /@ Characters[#] /.
{ {False, _} -> (# &), {True, True} -> ToUpperCase,
{True, False} -> ToLowerCase } & /@ #
],
Characters /@ #
}, 2] &
Interfacing: the trailing &
makes this a function. Its argument is inserted as the "#" at both instances of /@ #
. For instance f=0>1; ... & [{"AAAbbb111", "Cc2Dd3Ee4"}]
produces the output {AaABbb111,CC2dd3Ee4}
.
Processing: Told in usual outside in order:
- The output of the
MapThread[...]
is a list of two lists of characters. StringJoin is applied to each of these two lists of characters to produce a list of two strings, the output.
MapThread[#[#2]&, ... , 2]
acts on an array of two 2-by-n element lists. The first list is a 2-by-n array of functions. The second list is a 2-by-n array of characters, Characters /@ #
, the lists of characters in the two input strings. It works at depth 2, i.e., on the functions and individual characters.
Reverse[...]
swaps the two sublists of functions so that MapThread will apply the second string's functions to the first string and vice versa.
{ ... } &
is an anonymous function that is applied to each of the two input strings.
{LetterQ[#], UpperCaseQ[#]} & /@ Characters[#]
splits a string into a list of characters, then replaces each character with two element lists. In these two element lists, the first element is True
if the character is a letter and False
otherwise, similarly, the second element indicates whether the character is upper case. UpperCaseQ[]
cannot return true if it does not receive a letter.
/. {{False, _} -> (# &), {True, True} -> ToUpperCase, {True, False} -> ToLowerCase}
replaces these two element lists with functions. (Expansion of the abbreviations t
and f
occurs before any matching is attempted.) If a two element list has False
as its first element, it is replaced with the function (# &)
, the identity function. (The parentheses are necessary, otherwise the arrow binds more tightly than the ampersand.) Otherwise the two element list starts with True
, the character was a letter, and we output the functions ToUpperCase
and ToLowerCase
corresponding to its case. (Checking for this last False
is unnecessary, in fact {_,_}->ToLowerCase
would work, catching anything that hadn't been replaced yet, but this would be no shorter and more obscure.)
The only challenge was figuring out a succinct way to zip a two dimensional array of functions to an array of arguments.
Edit: Thanks to @Martin Büttner for catching "helpful" cut/paste linebreak backslashes, the 1>0
and 1<0
abbreviations, and also for the guidance to count length in bytes not characters (whatever those are :-) )
Edit2: Further thanks to @Martin Büttner for pointing out that polluting the global namespace is acceptable golf, reminding me of one character function application, and suggesting replacing the two uppercase functions with an abbreviation for one and using the one to emulate the other (saving four characters). (I think he's done this before. :-) )