26
\$\begingroup\$

We all know of different fancy sorting algorithms, but none of these give us numbers in a way that's easy to pronounce. To remedy this, I propose using PronunciationSort™, the most natural way to sort lists of numbers.

Pronunciation

The official rules for pronouncing numbers (in this challenge) is that the digits are pronounced one by one, and the resulting string is sorted in lexicographic order. As an example, this means that the number 845 is pronounced "eight four five", and should be sorted accordingly.

Negative numbers

Negative numbers are pronounced by prepending the word "minus". Thus, -23 is pronounced as "minus two three". Note that this causes negative numbers to end up in the middle of the output, right between numbers starting with 4 (four) and 9 (nine).

As a guide, the official order of words for PronunciationSort™ is:

  • eight
  • five
  • four
  • minus
  • nine
  • one
  • seven
  • six
  • three
  • two
  • zero

That is,

8, 5, 4, -, 9, 1, 7, 6, 3, 2, 0

Input

A list of integers in the range \$[-999, 999]\$, containing at most 100 elements. Input as a list of strings is not permitted. If your language does not support input as list, it is permissible to give input as separate integers.

The input will not contain any invalid numbers, or any number starting with a 0 (except the number 0 itself). The input will generally not be sorted, it can be given in any order.

Output

The same integers, in PronunciationSort™ order. Note that the numbers should only be converted to their pronunciations to get the sorting, the output should not contain any strings.

Examples

For the examples, the middle step (wrapped in parentheses) only serves as a guide, and is not a part of the output.

[1, 2, 3] -> (['one', 'two', 'three']) -> [1, 3, 2]
[-1, 0, 1, 2] -> (['minus one', 'zero', 'one', 'two']) -> [-1, 1, 2, 0]
[-100, 45, 96] -> (['minus one zero zero', 'four five', 'nine six']) -> [45, -100, 96]
[11, 12, 13, 134, 135] -> (['one one', 'one two', 'one three', 'one three four', 'one three five']) -> [11, 13, 135, 134, 12]

There's also a script for verifying your results.

\$\endgroup\$
14
  • 5
    \$\begingroup\$ Why doesn't "one" (pronounced "won") come after two and before zero? \$\endgroup\$
    – Ben Miller
    Commented Sep 27, 2018 at 18:50
  • 3
    \$\begingroup\$ @BenMiller I can't recall if it was you who commented in the sandbox, but that comment gave me deja vu. To answer it here, I contemplated it, but I went with spelling to avoid spelling discussions (e.g. "two" vs "too", "won" or "wan") \$\endgroup\$
    – maxb
    Commented Sep 27, 2018 at 19:57
  • 19
    \$\begingroup\$ So it is actually more "spelling sort" than "pronounciation sort" :-) \$\endgroup\$ Commented Sep 27, 2018 at 20:20
  • 3
    \$\begingroup\$ bummer. I was hoping they would be sorted by how difficult they are to pronounce... \$\endgroup\$
    – NH.
    Commented Sep 28, 2018 at 15:08
  • 2
    \$\begingroup\$ This challenge really should be renamed, since the sorting here has virtually nothing to do with pronunciation. It would be immensely more complex if it were pronunciation-based (e.g., four might come before five if you sort the monophthong ⟨ɔː⟩ before the diphthong ⟨aɪ⟩, but five before four if you sort a-derived ⟨a⟩ before o-derived ⟨ɔ⟩ – there is, to my knowledge, no established sorting order for pronunciation, neither in IPA nor in any other scheme). \$\endgroup\$ Commented Sep 29, 2018 at 14:09

20 Answers 20

10
\$\begingroup\$

Haskell, 57 bytes

import Data.List
sortOn$map(`elemIndex`"54-9176320").show

Try it online!

\$\endgroup\$
1
  • 1
    \$\begingroup\$ This is so clean and readable! Definitely the winner in terms of ease of understanding. \$\endgroup\$
    – slb
    Commented Oct 2, 2018 at 18:05
9
\$\begingroup\$

Jelly,  15  13 bytes

ṾV€ị“Þ⁽3Z6»µÞ

Try it online!

A monadic link accepting a list of integers which yields a list of integers.

How?

Sorts by the ordinal values of the digits of the integers (where - is a "digit" of -1) converted to strings using the characters at their 1-based & modular index in the magic string "murgeon lix".

The sort is effectively alphabetic where a space is considered less than any letter.

The magic string "murgeon lix" was found by inspecting Jelly's dictionaries used in compression. There are no words of 11 letters which satisfy the requirements (and none of more that would upon de-duplication). since a space sorts before letters the next most obvious choice is a word of length seven followed by a space followed by a word of length three. "murgeon" and "lix" is the only satisfying combination, although without a space others may be possible (e.g. “£Py:ƥ» is "murgeonalix" which works for the same byte-count)

ṾV€ị“Þ⁽3Z6»µÞ - Link: list of integers
            Þ - sort by:
           µ  -   the monadic link: -- i.e. do this for each integer, then sort by that
Ṿ             -     unevaluate  (e.g. -803 -> ['-','8','0','3'])
 V€           -     evaluate each as Jelly code  (e.g. ['-','8','0','3'] -> [-1,8,0,3])
    “Þ⁽3Z6»   -     "murgeon lix" (compression of words in Jelly's dictionary plus a space)
   ị          -     index into (1-indexed & modular) (e.g. [-1,8,0,3] -> "i xr")

Previous @ 15 bytes:

ṾV€ị“¡Zo⁶’Œ?¤µÞ

Here “¡Zo⁶’Œ?¤ finds the first permutation of natural numbers which would reside at the index 21,340,635 when all permutations of the numbers are sorted lexicographically - which is [6,10,9,3,2,8,7,1,5,4,11]. (“¡Zo⁶’ is a base 250 representation of 21340635, while Œ? does the calculation and ¤ groups these instructions together)

\$\endgroup\$
2
  • \$\begingroup\$ Even with the explanation, I don't feel very clever. Awesome solution! \$\endgroup\$
    – maxb
    Commented Sep 27, 2018 at 20:00
  • \$\begingroup\$ The shorter version is probably also easier to understand! \$\endgroup\$ Commented Sep 27, 2018 at 21:02
8
\$\begingroup\$

05AB1E (legacy), 15 bytes

Σε•Koéa₃•'-3ǝsk

Try it online!

Explanation

Σ                 # sort by
 ε                # apply to each
             sk   # index of the element in
  •Koéa₃•         # "85409176320"
         '-3ǝ     # with "-" inserted at index 3
\$\endgroup\$
4
  • \$\begingroup\$ There seems to be a bug in the compressed integer •ĆU‘•.. It adds a newline during the mapping/sorting for whatever reason. Σ•ĆU‘•"54-ÿ"sSk could have been a 15-byte alternative I was working on, if it weren't for that weird bug.. If I change •ĆU‘• to the literal 9176320 it works just fine.. \$\endgroup\$ Commented Sep 27, 2018 at 10:53
  • 1
    \$\begingroup\$ @KevinCruijssen: That is weird. Yours would be 14 with …54-ì even \$\endgroup\$
    – Emigna
    Commented Sep 27, 2018 at 11:35
  • \$\begingroup\$ @KevinCruijssen: You could do Σ•RT‹•Á…54-ìsSk for 15 \$\endgroup\$
    – Emigna
    Commented Sep 27, 2018 at 11:41
  • \$\begingroup\$ •t∍ýJ•'-ìÁÁ would also work \$\endgroup\$
    – Emigna
    Commented Sep 27, 2018 at 11:45
7
\$\begingroup\$

Perl 6, 30 bytes

*.sort:{TR/0..9-/a5982176043/}

Try it online!

Port of G B's Ruby solution.

Original 35 byte version

*.sort: (~*).uninames».&{S/\w*.//}

Try it online!

Convert each number to a string, get the Unicode name of each character, strip the first word ("DIGIT" or "HYPHEN"), then sort.

\$\endgroup\$
7
\$\begingroup\$

Python 3, 68 bytes 67 bytes 64 bytes

lambda x:sorted(x,key=lambda y:[*map('54-9176320'.find,str(y))])

Uses built-in sorted function with an anonymous lambda for the key. Hard-code the sort order and compare each digit in each value in the input list to its position in the sort order list.

Edit: Saved 1 byte by removing 8 from sort list to take advantage of str.find returning -1 when parameter is not found. Thanks to maxb.

Edit2: Saved 3 bytes by using starred unpacking syntax in a list literal instead of list constructor

Try it online!

\$\endgroup\$
3
  • 1
    \$\begingroup\$ Could you remove the first 8 in the string? As Python returns -1 if the substring is not found. \$\endgroup\$
    – maxb
    Commented Sep 27, 2018 at 16:11
  • \$\begingroup\$ @maxb Good catch. Edited. \$\endgroup\$
    – mypetlion
    Commented Sep 27, 2018 at 16:46
  • 2
    \$\begingroup\$ Python 2 port is 58: lambda x:sorted(x,key=lambda y:map('54-9176320'.find,`y`)) \$\endgroup\$ Commented Sep 27, 2018 at 21:30
6
\$\begingroup\$

JavaScript (SpiderMonkey), 69 bytes

a=>a.sort((a,b)=>(g=n=>[...n+''].map(c=>':598217604'[c]||3))(a)>g(b))

Try it online!

\$\endgroup\$
3
  • \$\begingroup\$ It looks like you can remove the +'', seeing as you're taking input as an array of strings. \$\endgroup\$
    – Shaggy
    Commented Sep 27, 2018 at 10:01
  • \$\begingroup\$ @Shaggy I'm not taking strings anymore, as I'm not sure if that's allowed here. \$\endgroup\$
    – Arnauld
    Commented Sep 27, 2018 at 10:01
  • \$\begingroup\$ Ah ... You're right. That's gonna add a couple of bytes to my solution. \$\endgroup\$
    – Shaggy
    Commented Sep 27, 2018 at 10:03
6
\$\begingroup\$

K (ngn/k), 21 20 22 bytes

{x@<"54-9176320"?/:$x}

Try it online!

{ } function with argument x

$ format as strings

"..."?/: find indices of individual characters in the given string (or \$-2^{63}\$ for not found - note that the "8" is missing)

< compute sort-ascending permutation

x@ the argument at those indices

\$\endgroup\$
5
\$\begingroup\$

Pyth, 17 16 bytes

oxL"54-9176320"`

Try it online here, or verify all the test cases at once here.

oxL"54-9176320"`NQ   Implicit: Q=eval(input())
                     Trailing N, Q inferred
o                Q   Order the elements of Q, as N, using...
               `N      Convert N to string
 xL                    Get the index of each character of that string...
   "54-9176320"        ... in the lookup ordering
                       (if character missing, returns -1, so 8 is still sorted before 5)

Saved 1 byte thanks to @ngn and their K answer, by omitting 8 from the start of the dictionary string

\$\endgroup\$
4
\$\begingroup\$

Japt, 19 bytes

ñ_s ®n"54-9176320

Try it

\$\endgroup\$
3
  • \$\begingroup\$ Nice solution! Input as a list of strings is not allowed unfortunately. \$\endgroup\$
    – maxb
    Commented Sep 27, 2018 at 10:55
  • \$\begingroup\$ You can save some by abusing S.n(s): ñ_s ®n"54-9176320 (apparently S.n(s) is exactly the same as s.b(S) for S of length 1, except it returns 0 in place of -1) \$\endgroup\$ Commented Sep 28, 2018 at 2:58
  • \$\begingroup\$ Nice trick, @ETHproductions, thanks :) I'll have to remember that one for the future. \$\endgroup\$
    – Shaggy
    Commented Sep 28, 2018 at 10:14
3
\$\begingroup\$

Retina 0.8.2, 36 bytes

T`-d`3:598217604
O`
T`3:598217604`-d

Try it online! Link includes test suite. Explanation:

T`-d`3:598217604

Translate the minus sign and digits to their position in pronunciation order, using : for 10th position.

O`

Sort in pronunciation order.

T`3:598217604`-d

Translate the order back to the original minus sign and digits.

\$\endgroup\$
3
\$\begingroup\$

Ruby, 50 bytes

->a{a.sort_by{|x|x.to_s.tr('0-9-','a5982176043')}}

Try it online!

\$\endgroup\$
3
\$\begingroup\$

C (GCC), 228 bytes

#define A(i,a)sprintf(a,"%d",i)
#define U "854-9176320"
#define O(c)(strchr(U,c)-U)
S(int*a,int*b){char c[5],*x=c,e[5],*y=e,d;A(*a,x);A(*b,y);do{d=O(*x)-O(*y);x++;y++;}while(!d&&*x&&*y);return d;}F(int*v,int c){qsort(v,c,4,S);}

More readable & runnable version

Not really a contender, but fun anyway. Assumes 4-byte ints, but substituting 8 doesn't change the code size. Converting numbers back to strings is even more of a hassle in C than the other way. And the complete program is even converting the string arguments to numbers, just to have them converted back to strings in the sorter.

\$\endgroup\$
1
3
\$\begingroup\$

R, 58 bytes

function(x)x[order(mapply(chartr,"-0-9","dkfjicbhgae",x))]

Try it online!

Input is a list of numbers which is implicitly converted as string using chartr. order then uses lexigographic order to retrieve the order by which the original list should be sorted.

\$\endgroup\$
3
\$\begingroup\$

Java (JDK 10), 123 bytes

l->l.sort(java.util.Comparator.comparing(n->{var r="";for(var c:(""+n).split(""))r+=11+"54-9176320".indexOf(c);return r;}))

Try it online!

This is a naive Java implementation. Should be much golfable.

Credits

\$\endgroup\$
1
  • 1
    \$\begingroup\$ Changing the .chars-IntStream and .reduce to a regular loop saves 2 bytes: n->{var r="";for(var c:(""+n).split(""))r+=10+"854-9176320".indexOf(c);return r;}. In addition, one more byte can be saved by changing 10+"85 to 20+"5, since the .indexOf for digit 8 would then result in -1. Try it online 123 bytes \$\endgroup\$ Commented Sep 27, 2018 at 16:58
2
\$\begingroup\$

JavaScript (SpiderMonkey), 87 73 bytes

a=>a.sort((p,q,F=b=>[...""+b].map(x=>"54-9176320".search(x)))=>F(p)>F(q))

Try it online!

Thanks @Arnauld for telling that the sort in SpiderMonkey is stable, so the ||-(F(q)>F(p)) part can finally be dropped.

\$\endgroup\$
2
\$\begingroup\$

Red, 114 bytes

func[n][g: func[a][collect[foreach c form a[keep index? find"854-9176320"c]]]sort/compare n func[x y][(g x)< g y]]

Try it online!

More readable:

f: func [ n ] [
    g: func [ a ] [
        collect [ 
            foreach c form a [ 
                keep index? find "854-9176320" c
            ]
        ]
    ]
    sort/compare n func [ x y ] [ (g x) < g y ]
]
\$\endgroup\$
2
\$\begingroup\$

C++, 353 bytes

This is kind of a comedy entry, but I was wasting time and wrote it, so I can't not post it... Enjoy a chuckle, and let me know if there are any space-savers I missed!

#include<algorithm>
#include<iostream>
#include<iterator>
#include<numeric>
#include<string>
using namespace std;auto f(int i){auto s=to_string(i);for(auto&c:s)c='A'+"854-9176320"s.find(c);return s;}int main(){int a[100];auto b=begin(a);auto e=end(a);iota(b,e,-50);sort(b,e,[](int l,int r){return f(l)<f(r);});copy(b,e,ostream_iterator<int>(cout," "));}

Output:

8 5 4 48 45 44 49 41 47 46 43 42 40 -8 -5 -50 -4 -48 -45 -44 -49 -41 -47 -46 -43 -42 -40 -9 -1 -18 -15 -14 -19 -11 -17 -16 -13 -12 -10 -7 -6 -3 -38 -35 -34 -39 -31 -37 -36 -33 -32 -30 -2 -28 -25 -24 -29 -21 -27 -26 -23 -22 -20 9 1 18 15 14 19 11 17 16 13 12 10 7 6 3 38 35 34 39 31 37 36 33 32 30 2 28 25 24 29 21 27 26 23 22 20 0

\$\endgroup\$
4
  • \$\begingroup\$ I see you also live by the motto "if I don't press enter there are fewer lines to debug" \$\endgroup\$
    – maxb
    Commented Sep 30, 2018 at 6:30
  • 1
    \$\begingroup\$ Hey, I can't afford frivolous whitespace in this language! It's funny to have to do this, seeing as the rest of the time, few things make me as angry as opening up others' code, who apparently think writing a horrifying monolithic wall will make them seem smarter, and/or are actively docked pay for every newline they type. \$\endgroup\$ Commented Sep 30, 2018 at 10:40
  • 1
    \$\begingroup\$ Hello! Squeezed your solution to 195 characters \$\endgroup\$ Commented Oct 2, 2018 at 5:53
  • \$\begingroup\$ @MaxYekhlakov Cool, thanks for pondering it! I realise after reading around a bit more; it seems that I needn't necessarily have provided a full compilable program, but instead only the functions that would process the specified input and output. D'oh! \$\endgroup\$ Commented Oct 2, 2018 at 19:27
2
\$\begingroup\$

Mathematica, 68 bytes

SortBy[If[# < 0,"m ",""]<>StringRiffle@IntegerName@IntegerDigits@#&]

Function. Takes a list of integers as input and returns the sorted list as output. Just separates the digits of each number with IntegerDigits, converts each digit to "zero", "one", etc., with IntegerName, converts the list into a space-separated string with StringRiffle, prepends an "m " if the number is negative, and sorts based on this string. Indeed, this was the shortest approach I can find, since Mathematica only natively uses lexicographic sorting for lists of the same length; thus, an approach based on 854-9176320 ends up taking more bytes since string functions are so expensive.

\$\endgroup\$
1
  • \$\begingroup\$ Always trust mathematica to have a combination of builtins. Clever solution! \$\endgroup\$
    – maxb
    Commented Sep 30, 2018 at 10:55
1
\$\begingroup\$

05AB1E, 15 14 13 bytes

Σ•t∍ýJ•(ÁÁsSk

-1 byte thanks to @Emigna.
-1 byte thanks to @ovs.

Try it online or verify all test cases.

Explanation:

Σ              # Sort by:
 •t∍ýJ•        #  Push compressed integer 917632054
       (       #  Negate it: -917632054
        ÁÁ     #  Rotate it twice towards the right: "54-9176320"
          s    #  Swap so the current number to sort is at the top of the stack
           S   #  Convert it to a list of characters
            k  #  Check for each its index in the string (resulting in -1 for '8')

See this 05AB1E tip of mine (section How to compress large integers) to understand why •t∍ýJ• is 917632054.

\$\endgroup\$
1
  • 1
    \$\begingroup\$ 13 bytes by rotating -917632064 instead of prepending a string. \$\endgroup\$
    – ovs
    Commented Jan 18, 2021 at 22:40
1
\$\begingroup\$

Nim, 110 109 105 103 bytes

import algorithm,sequtils,strutils
func p[S](n:S):S=n.sortedByIt join mapIt($it,$"54-9176320".find(it))

Try it online!

For some reason, Nim can't compare seqs, so I had to turn the results into a string, which cost quite a lot of bytes.

\$\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.