Given:
strings = {"send", "more", "money"};
letterMap = Flatten[Characters[strings]] // DeleteDuplicates
(*Example: {"s", "e", "n", "d", "m", "o", "r", "y"}*)
numberMap = RandomSample[Range[0, 9], 8]
(*Example: {7, 3, 6, 9, 5, 0, 4, 1}*)
I would like to convert these strings to integers as determined by the mapping specified by letterMap and numberMap.
For example, "send" should be equal to 7369 because s->7, e->3, n->6, d->9.
I have done this three different ways, but none of my solutions are computationally efficient enough. Can you find a more efficient method?
From Fastest to Slowest:
Using Associations and Lookup (Method 2):
StringToNumber2[str_String, letters_List, numbers_List] :=
(
Clear[rules, assoc];
rules = Thread[Rule[letters, numbers]];
assoc = Association[rules];
FromDigits[Lookup[assoc, #] & /@ Characters[str]]
)
Using Rules and ReplaceAll (can be improved a little using Thread like above) (Method 0):
StringToNumber0[str_String, letterMap_List, numberMap_List] :=
(
Clear[rules];
rules = Rule @@@ Partition[Riffle[letterMap, numberMap], 2];
FromDigits[Characters[str]] /. rules
)
Using Position and Part (Method 1):
StringToNumber1[str_String, letterMap_List, numberMap_List] :=
(
FromDigits[
numberMap[[Flatten[Position[letterMap, #]]]] & /@ Characters[str] //
Flatten]
)
Expected output, given inputs above:
{7369, 5043, 50631}
Most importantly, do you have any tips in improving efficiency of Mathematica code when faced with problems like this?
(Update after receiving wonderful feedback!) Henrik's Compiled SparseArray method was definitely the quickest, although I'm wondering how much improvement of other methods could be realized by utilizing Compile!
Below was how I measured the performance of each method:
(*See how long the algorithms take to analyze 100,000 pieces of data*)
Column[{Table[StringToNumber0[strings, letters, numbers], 100000]; //
AbsoluteTiming,
Table[StringToNumber1[strings, letters, numbers], 100000]; //
AbsoluteTiming,
Table[StringToNumber2[strings, letters, numbers], 100000]; //
AbsoluteTiming,
Table[StringToNumber3[strings, letters, numbers], 100000]; //
AbsoluteTiming,
Table[StringToNumber4[strings, letters, numbers], 100000]; //
AbsoluteTiming
}]
Where Method 3 = Carl's method, and Method 4 = Henrik's method.
{
{{4.79445, Null}}, (*Method 0*)
{{8.75677, Null}}, (*Method 1*)
{{2.04422, Null}}, (*Method 2*)
{{1.75599, Null}}, (*Method 3*)
{{0.900393, Null}}, (*Method 4*)
}
//AbsolutTiming
suggestsStringToNumber2
is the fastest. But I'm curious about 2 things: (1) Do you need to convert back - assuming this is some sort of secret decoder ring method, and (2) Does it matter if there are more than 8 (or especially more than 9) unique characters? $\endgroup$StringToNumber3
andStringToNumber4
. $\endgroup$