# Lexicographic ordering of strings in Mathematica

I recently realized that Mathematica seems incapable of comparing strings in the "normal" expected lexicographic order. Indeed, for some simulations, I need to process text directly, without fiddling with it, and I would like to have such things as

If["aaa" < "aaaab", 1, 0] (* ---> 1 *)
Min["aaaa", "deaaaf", "dfeef", "a"]   (* ---> "a" *)


and so on. However, as far as I can tell this is not possible. Am I wrong? Or is there any work-around, or way for me to use lexicographic ordering? Do I have to code it by hand? Mathematica is so good at getting stuff right out of the box I have a hard time believing it (but this would echo some other contributors comment here, that strings are "second class" citizens in Mathematica)...

-

I believe you are looking for Order and Ordering.

The 1 indidcates that "aaa" comes before "aaaab" in canonical ordering:

Order["aaa", "aaaab"]

1


Here Ordering is used to get the position of the first element in the sorted list and the that element is extracted from the list. This is equivalent to a "Min" function.

list = {"aaaa", "deaaaf", "dfeef", "a"};
list ~Extract~ Ordering[list, 1]

"a"


And a "Max" function:

list ~Extract~ Ordering[list, -1]

"dfeef"

-
And perhaps stringOrderedQ[s1_String, s2_String] := OrderedQ[Hold[s1, s2]], so that stringOrderedQ["aaa", "aaaab"] == True/stringOrderedQ["aaaab", "aaa"] == False. – Oleksandr R. Feb 28 '12 at 20:21
@Oleksandr That is noteworthy. I chose Order because the OP was already looking for integer output (1, 0), and because Order provides more information in the case of a tie. – Mr.Wizard Feb 28 '12 at 20:27
@OleksandrR. Why do you use Hold if you already have _String on the lhs? – Szabolcs Feb 28 '12 at 20:28
@Szabolcs no particular reason. OrderedQ works for expressions with any Head, so anything without the Orderless attribute will do. List would have been fine, in fact. – Oleksandr R. Feb 28 '12 at 20:35

You can do something like:

Unprotect[Less,LessEqual,Greater,GreaterEqual];
Less[s1_String,s2_String] := Order[s1,s2]>0;
LessEqual[s1_String,s2_String] := Order[s1,s2]>-1;
Greater[s1_String,s2_String] := Order[s1,s2]<0;
GreaterEqual[s1_String,s2_String] := Order[s1,s2] <1;
Protect[Less,LessEqual,Greater,GreaterEqual];


This will make your If[] example work, at least. Would have to do some similar override for Min[] and other functions that you want to work "naturally" on strings.

-
I wouldn't recommend redefining built-in functions globally. Here is just one example of how it can go wrong, and all the operators you overload are pretty fundamental. You can reduce the risks by introducing local environments, e.g. like in this answer (which seems also generally on-topic to link here).See also this discussion – Leonid Shifrin Feb 28 '12 at 20:30
It is not such a good idea to modify built-ins. You never know what code relies on "acb" > "def" not evaluating (I certainly do have such code myself, which I was running today: I substitute in values for the strings using ReplaceAll from a parameter list). See scary example here and this post – Szabolcs Feb 28 '12 at 20:32
@Leonid Ah, you replied the same while I was writing my comment. I actually happen to have used code today that would break with this modification. I was storing "metadata" (parameter values used to generate them) in my data files in the "string" -> value format, and did "param" > 0 /. paramlist. – Szabolcs Feb 28 '12 at 20:35
@Szabolcs You see, even knowing this very well, it is still easy to overlook. I am usually taking a defensive approach here: try to code such that things like that can never happen, and so that I could prove that with more or less mathematical rigor. In other words, I am actively looking for weaknesses like that it my code when I write it, because many times even a small chance for trouble was enough to get into it. – Leonid Shifrin Feb 28 '12 at 20:40