Suppose I have a numberic string

str = "1234567456";

and another string called

nstr = "05eccead24";

I want to transform the non-digit character in nstr, that number corresponding to the same position in str is even, to upper cases. For example, the first non-digit character in nstr is e, and the position is 3, the same postion in str is 3 which is odd, thus we keep it in lowercase. While for the next char c in nstr, the position is 4 and the corresponding character in str is 4, which is even, so we should change it into uppercase.

The expected output should be:


This works:

MapThread[If[EvenQ[#2], ToUpperCase[#1], #1] &, {Characters @ "05eccead24", 
          IntegerDigits @ FromDigits @ "1234567456"}] // StringJoin

(* "05eCcEaD24" *)
  • 1
    $\begingroup$ Why community wiki? Should I have added my method rather than posting separately? $\endgroup$ – Mr.Wizard Jul 15 '16 at 2:36
  • 1
    $\begingroup$ @Mr. Wizard, fret not, I've upvoted yours. I know mine will work, but I cannot test it at the moment (currently using the gedanken version). $\endgroup$ – J. M.'s torpor Jul 15 '16 at 2:38
  • $\begingroup$ It seems your method is orders of magnitude faster than mine. If I cannot improve that I'll probably delete my answer. Incidentally a line cannot start with //. I hope you don't mind if I reformat your code. $\endgroup$ – Mr.Wizard Jul 15 '16 at 2:49
  • $\begingroup$ I rewrote my code to avoid StringReplacePart and it is now ~2.9 times faster than this. $\endgroup$ – Mr.Wizard Jul 15 '16 at 3:15
  • 1
    $\begingroup$ You do know that quite a number of my answers last year were done without my having access to Mathematica, no? ;) $\endgroup$ – J. M.'s torpor Jul 16 '16 at 14:45

Edit: I first used StringReplacePart but that function is slow when used for many replacements, much as MapAt is. I have rewritten my code using a method from Map a function across a list conditionally and it is now orders of magnitude faster on long strings.

f[source_String, target_String] :=
  Module[{new = Characters @ target},
    (new[[#]] = ToUpperCase @ new[[#]]) & @
       StringPosition[source, Characters @ "02468"][[All, 1]];
    new <> ""

f["1234567456", "05eccead24"]
  • $\begingroup$ Maybe DigitCharacter and EvenQ can replace that long stream of text,but I have no ideal $\endgroup$ – yode Jul 15 '16 at 2:49
  • $\begingroup$ @yode Literal patterns are typically faster however. $\endgroup$ – Mr.Wizard Jul 15 '16 at 3:12
  • $\begingroup$ It is very good answer, but to me (I will not concern the time consumming stuff) @J.M. 's answer is more clean (and easy to understand). $\endgroup$ – van abel Jul 15 '16 at 3:17
  • 1
    $\begingroup$ @vanabel Thanks, and no problem; I like clean code too. :-) $\endgroup$ – Mr.Wizard Jul 15 '16 at 3:18
  • 2
    $\begingroup$ @ShutaoTANG Certainly that is true in many areas, though there are also cases where Mathematica is competitively fast, usually when operating on packed arrays with calls to fast library (Intel MKL, etc.) functions. However why are you commenting this here? Edit: Oh, I see you wrote a fast answer. +1 on that. I wonder if I can improve on my pure-Mathematica method. $\endgroup$ – Mr.Wizard Jul 17 '16 at 3:10

Should be quick for large strings if you want to keep it native MMA:

upem[s_, t_] := 
  Module[{ss = Pick[Range@StringLength@s, EvenQ@ToCharacterCode[s]], 
    tc = ToCharacterCode@t},
   tc[[Intersection[ss, Pick[Range@StringLength@t, Unitize@Clip[tc, {97, 122}, {0, 0}],1]]]] -= 32;


upem["1234567456", "05eccead24"]

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.