6
$\begingroup$

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:

"05eCcEaD24"
$\endgroup$

3 Answers 3

12
$\begingroup$

This works:

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

(* "05eCcEaD24" *)
$\endgroup$
11
  • 1
    $\begingroup$ Why community wiki? Should I have added my method rather than posting separately? $\endgroup$
    – Mr.Wizard
    Jul 15, 2016 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$ Jul 15, 2016 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, 2016 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, 2016 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$ Jul 16, 2016 at 14:45
7
$\begingroup$

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"]
"05eCcEaD24"
$\endgroup$
6
  • $\begingroup$ Maybe DigitCharacter and EvenQ can replace that long stream of text,but I have no ideal $\endgroup$
    – yode
    Jul 15, 2016 at 2:49
  • $\begingroup$ @yode Literal patterns are typically faster however. $\endgroup$
    – Mr.Wizard
    Jul 15, 2016 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, 2016 at 3:17
  • 1
    $\begingroup$ @vanabel Thanks, and no problem; I like clean code too. :-) $\endgroup$
    – Mr.Wizard
    Jul 15, 2016 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, 2016 at 3:10
1
$\begingroup$

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;
   FromCharacterCode@tc];

E.g.

upem["1234567456", "05eccead24"]
$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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