19
\$\begingroup\$

Alphabetize Integers

For a given set of numbers, put them in alphabetical order when they are spelled out (i.e. 1: one, 2: two, 90: ninety, 19: nineteen). Your code should work for the range [-999999, 999999]. Output must have a delimiter between numbers. A space will work, as will a space and a comma as shown in the examples below. Input can be an array of integers, a string of delimited numbers, or however you see fit. All integers are assumed to be unique.

Numbers are not hyphenated for the purposes of this challenge and spaces are alphabetized before any other characters. Negative numbers are assumed to be expressed by using the word minus. For example, four would precede four thousand and the number -40 would be sorted using the string minus forty. Assume all numbers will be solely comprised of number words and no conjunctions (e.g. use two thousand forty two instead of two thousand and forty two).


Test Cases

Single Digit Integers:

Input:

1, 2, 3, 4, 5

Output:

5, 4, 1, 3, 2

Multiple Digit Integers:

Input:

-1002, 5, 435012, 4, 23, 81, 82

Output:

81, 82, 5, 4, 435012, -1002, 23

Spaces between words, no hyphens, commas or "and":

Input:

6, 16, 60, 64, 600, 6000, 60000, 60004, 60008, 60204, 60804

Output:

6, 600, 6000, 16, 60, 64, 60000, 60008, 60804, 60004, 60204

Remember, this is , so the code with the fewest bytes wins. No loopholes allowed!

\$\endgroup\$
  • \$\begingroup\$ Here is the link to the relevant sandbox post. \$\endgroup\$ – wubs Dec 1 '16 at 20:23
  • \$\begingroup\$ Will the input ever contain more than one of a single integer? \$\endgroup\$ – ETHproductions Dec 1 '16 at 20:42
  • \$\begingroup\$ @ETHproductions No, it will not. I'll specify that in the question. \$\endgroup\$ – wubs Dec 1 '16 at 20:43
  • 8
    \$\begingroup\$ Welcome to PPCG. Nice avatar. :D Nice first question. \$\endgroup\$ – AdmBorkBork Dec 1 '16 at 20:50
  • \$\begingroup\$ @TimmyD Thanks! I'm looking forward to PowerShell-ing everything I can around here. \$\endgroup\$ – wubs Dec 1 '16 at 21:05
5
\$\begingroup\$

JavaScript (ES6), 189 179 186 bytes

let f =

a=>a.sort((x,y)=>!x-!y||(X=q(x),Y=q(y),X>Y)-(X<Y),q=n=>n<0?"L"+q(-n):n>999?q(n/1e3)+"Z"+q(n%1e3):n>99?q(n/100)+"K"+q(n%100):n>19?"  cYHFVSCO"[n/10|0]+q(n%10):"0PdaIGTQAMWDbXJEURBN"[n|0])

let g = a => console.log(`[${f(a)}]`)

g([1,2,3,4,5])
g([-1002,5,435012,4,23,81,82])
g([0,1000,1100])
<input id=I value="1 2 3 4 5"><button onclick="g(I.value.match(/\d+/g)||[])">Run</button>

The basic idea is to convert each input number into a short string that's in the correct lexographical position compared to all other number-string pairs. Here's the dictionary used: (Don't run the snippet; it's just used to hide the long list.)

A eight
B eighteen
C eighty
D eleven
E fifteen
F fifty
G five
H forty
I four
J fourteen
K hundred
L minus
M nine
N nineteen
O ninety
P one
Q seven
R seventeen
S seventy
T six
U sixteen
V sixty
W ten
X thirteen
Y thirty
Z thousand
a three
b twelve
c twenty
d two

This creates a very concise way of mapping each number to its lexographically correct position. That's what the recursive q function does:

q(-X)        => "L" + q(X)
q(XYYY)      => q(X) + "Z" + q(YYY)
q(XYY)       => q(X) + "K" + q(YY)
q(XY >= 20)` => "  cYHFVSCO"[X] + q(Y)
q(X)         => "0PdaIGTQAMWDbXJEURBN"[X]

The 0 at the beginning of the string is to ensure that e.g. 100 (one hundred, converted to PK0) is sorted before 101 (one hundred one, converted to PKP). This creates an odd scenario where 0 (zero) is sorted to the front of the array, so to get around this, in the sorting function we first sort any zeroes to the right with !x-!y||(....

\$\endgroup\$
  • \$\begingroup\$ Looks like it doesn't work for [1100, 1000]. I would expect the output to be 1000 (one thousand), 1100 (one thousand one hundred), but the output is the same order as input. \$\endgroup\$ – milk Dec 1 '16 at 21:38
  • \$\begingroup\$ @milk Hmm... I'm not sure why this happens, but I'll look into it. \$\endgroup\$ – ETHproductions Dec 1 '16 at 22:18
  • \$\begingroup\$ @milk Ah, 1000 is being parsed as one thousand zero; I'll fix this momentarily. Must we support 0 on its own though? It's a unique case that will add 15 bytes or so to my code. \$\endgroup\$ – ETHproductions Dec 1 '16 at 22:22
11
\$\begingroup\$

Inform 7, 214 201 118 bytes

Inform 7 is an absolutely terrible language for golfing, so I wanted to give it a chance here.

Indentation should use tab (\t) characters, but HTML doesn't like those. Conversely, Inform doesn't like spaces for indentation, so you'll have to replace the spaces with tabs if you copy-paste the code from here to test it. Or just copy-paste from the Markdown source instead.

Golfed:

To X:
    repeat through Table 1:
        now Q entry is "[R entry in words]";
    sort Table 1 in Q order;
    say "[R in Table 1]".

Input should be an Inform table, like so (with \t between the columns):

Table 1
R (number)  Q (text)
-1002
5
435012
4
23
81
82

Output:

81, 82, 5, 4, 435012, -1002, 23

This function runs through the table once, adding a textual representation of each number in a new column. Then it sorts the table rows according to the text column; in Inform, strings are sorted lexicographically. Finally, it prints out the original column in the new order. Conveniently, Inform 7's "crude but sometimes useful" format for printing out table columns turns out to be comma separated, exactly as requested.

Ungolfed, with boilerplate showing how to call the function:

To print the numbers in alphabetical order:
    repeat through the Table of Sortable Numbers:
        now the name entry is "[the index entry in words]";
    sort the Table of Sortable Numbers in name order;
    say "[the index column in the Table of Sortable Numbers]".

Table of Sortable Numbers
index (number)  name (text)
-1002
5
435012
4
23
81
82

There is a room.
When play begins: print the numbers in alphabetical order.
\$\endgroup\$
  • 1
    \$\begingroup\$ I'm slightly confused by this. Is words a reference of the spelled out versions of numbers, built into Inform 7? \$\endgroup\$ – Pavel Dec 1 '16 at 20:52
  • 1
    \$\begingroup\$ @Pavel Indeed! "(number) in words" returns a string with a textual representation of the number. It conveniently uses "minus" for negative numbers, and while it puts hyphens between words, it does so consistently and alphabetizes hyphens before all letters (so the end result is the same). \$\endgroup\$ – Draconis Dec 1 '16 at 20:57
  • 2
    \$\begingroup\$ +1 for the language choice. I'd have to check, but I suspect there are some golfing opportunities left, though; for example, does the parser really require all those "the" articles? And you'd have to ask the OP, but I don't see any obvious reason why " and " would not be a valid delimiter. Even if not, a single space is explicitly allowed, so just say "[R entry] " should suffice. \$\endgroup\$ – Ilmari Karonen Dec 1 '16 at 23:01
  • \$\begingroup\$ I'd say the "and" at the end is fine. I didn't say that the delimiters had to be uniform, so this is a perfectly acceptable answer. If I could give points for most interesting answer, I would give it to you. I really enjoy the readability of this language, even golfed. Nice job! \$\endgroup\$ – wubs Dec 2 '16 at 18:56
  • \$\begingroup\$ I finally got a chance to play with Inform7 a bit, and managed to re-golf your entry down to just 118 bytes. Since posting Inform code in comments doesn't work very well, I went ahead and edited it into your answer directly. I hope you don't mind, feel free to revert and/or tweak my edits any way you like if you do. \$\endgroup\$ – Ilmari Karonen Dec 10 '16 at 19:25
4
\$\begingroup\$

Mathematica, 67 bytes

SortBy[#,#~IntegerName~"Words"~StringReplace~{","->"","-"->""}&]&

Unnamed function taking a list of integers as its argument and returning a list of integers as its value. #~IntegerName~"Words" is a built-in that changes an integer to its name in English. IntegerName sometimes has commas and hyphens in its output, so the StringReplace call gets rid of those. (Sadly the hyphen is actually the 3-byte character, 8208, in UTF-8.) Then SortBy sorts the original list alphabetically according to the value of the modified integer name.

A nice coincidence: IntegerName uses negative instead of minus in its output—but no word appearing in the names of any of the allowed numbers is alphabetically between those two words, so no replacement is needed!

(Hat tip to ngenisis for reminding me of Sortby.)

\$\endgroup\$
  • \$\begingroup\$ Kudos! I came very close to getting this solution, but that dash was giving me headaches! \$\endgroup\$ – ngenisis Dec 1 '16 at 22:12
  • \$\begingroup\$ Does your answer here actually use the correct hyphen? If I copy what you have here into Mathematica it doesn't replace the hyphens from IntegerName. The Wolfram documentation says that it is unicode character 2010. \$\endgroup\$ – ngenisis Dec 1 '16 at 22:38
  • \$\begingroup\$ Probably not, then—I tried to get the correct hyphen in this answer, but looks like I didn't succeed. \$\endgroup\$ – Greg Martin Dec 2 '16 at 2:11
  • \$\begingroup\$ I cut your answer in half ;) \$\endgroup\$ – J. Antonio Perez Dec 2 '16 at 15:27
  • \$\begingroup\$ And then some... you make unnecessary modifications to the string. \$\endgroup\$ – J. Antonio Perez Dec 2 '16 at 15:28
4
\$\begingroup\$

Bash + GNU utils + bsdgames, 52

  • 4 bytes saved thanks to @izabera.
sed 's/.*/echo `echo &|number`:&/e'|sort|sed s/.*://

I/O is newline-delimited lines.

  • The first sed expression replaces each numerical number with a shell command that outputs the word form of the number (as given by the bsdgames number utility), followed by a : then the numerical form of the number.
  • This is then sorted.
  • Then the second sed strips leading characters up to and including the :, leaving the numerical form sorted as required.

number correctly handles "minus", and its output is close enough to the specificed format that the sort works as required. It does output "fourty-four" instead of "fourty four", but this shouldn't matter from the sorting perspective.

The bsdgames package may need installation:

sudo apt-get install bsdgames

The sed and sort utilities are almost certainly already in your distro.

\$\endgroup\$
  • \$\begingroup\$ -t: is useless and you can use number<<<& \$\endgroup\$ – izabera Dec 2 '16 at 2:06
  • \$\begingroup\$ @izabera Yes - thanks - I removed the -t:. However, sed's eval feature runs commands using sh, so bash features like <<< won't work. \$\endgroup\$ – Digital Trauma Dec 2 '16 at 17:38
  • \$\begingroup\$ it works fine as long as your sh is bash :P \$\endgroup\$ – izabera Dec 2 '16 at 22:49
  • \$\begingroup\$ @izabera Nope - if bash as started as sh it tries to emulate Posix sh as much as possible, which means that bashisms such as <<< are turned off. GNU sed's eval feature starts commands with /bin/sh -c ... and not /bin/bash -c .... Have you tried this? \$\endgroup\$ – Digital Trauma Dec 2 '16 at 23:32
  • \$\begingroup\$ bash never turns off <<<, not even in posix mode \$\endgroup\$ – izabera Dec 3 '16 at 0:13
1
\$\begingroup\$

Python + inflect, 97 91 89 bytes

from inflect import*
a={x:engine().number_to_words(x)for x in words}
sorted(a,key=a.get)

Used the inflect library to transform the words array of integers into their phonetic/string representation. Stored into a dictionary of k/v pairs where the keys were the numeric representation and values were the string representation. Returned the list of keys as sorted by values.

EDIT: Saved 5 and 3 bytes, thanks to ETHproductions and Alex.S!

\$\endgroup\$
  • \$\begingroup\$ Welcome to PPCG! You can golf this by removing spaces; for example, the second line can be a={x:inflect.engine().number_to_words(x)for x in words}. \$\endgroup\$ – ETHproductions Dec 2 '16 at 4:06
  • \$\begingroup\$ You can save two bytes using from inflect import* and throwing out inflect. in the second line. \$\endgroup\$ – Alex.S Dec 2 '16 at 11:15
  • \$\begingroup\$ Alas, looks like this also fails to correctly sort the list 40, 44, 40000, 40804, 40004, 40204 (which should stay in that order). \$\endgroup\$ – Ilmari Karonen Dec 10 '16 at 20:13
0
\$\begingroup\$

Mathematica, 30 bytes

The answer below outputs a pure function which will take a list of integers as an input and sort them by their alphabetic name. Just what the doctor ordered ;)

SortBy[#~IntegerName~"Words"&]

Here is the ungolfed version:

SortBy[IntegerName[#, "Words"]&]

And here is an example usage:

SortBy[#~IntegerName~"Words"&][{0,1,2,3,4,5,6,7,8,9,10}]

Which could also be written as

SortBy[#~IntegerName~"Words"&]@{0,1,2,3,4,5,6,7,8,9,10}

They produce identical outputs - in mathematica, f[x] is equivilant to f@x.

Outputs: {8, 5, 4, 9, 1, 7, 6, 10, 3, 2}

There is a much longer answer that another user posted in Mathematica. That answer tries to correct for some small differences between the way mathematica alphebatizes numbers to better conform to the way the OP stated numbers should be alphebatized, however the things they correct for don't affect sorting order, and my answer outputs identically to theirs:

MyF = SortBy[#~IntegerName~"Words"&];
TheirF = SortBy[#, #~IntegerName~"Words"~ StringReplace~{"," -> "", "-" -> ""} &] &;
MyF[Range[-999999, 999999]] == TheirF[Range[-999999, 999999]]
(*Outputs True*)
\$\endgroup\$
  • \$\begingroup\$ A great investigation!—unfortunately, they don't actually give the same order. TheirF correctly sorts 888 before 880,000, while MyF doesn't. Probably the issue is with the copy-pasting of the strange hyphen: your version of TheirF is probably replacing normal hyphens (of which there are none), while the actual version replaces the strange 3-byte Unicode hyphen. (It would still be interesting to see whether removing commas is necessary.) \$\endgroup\$ – Greg Martin Dec 2 '16 at 17:59
  • \$\begingroup\$ I tested it on Range[999999]. Looks like eliminating the commas is unnecessary, but replacing "[Hyphen]" with "" is definitely necessary. \$\endgroup\$ – ngenisis Dec 2 '16 at 19:28
0
\$\begingroup\$

Common Lisp, 113 bytes

No external libraries necessary.

(print(mapcar #'cdr(sort(loop for i in x collect(cons(format()"~r"i)i))(lambda(y z)(string-lessp(car y)(car z))))))

Output if x is '(1 2 3 4 5):

(5 4 1 3 2)
\$\endgroup\$

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.