PronunciationSort™

24

4

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.

maxb

Posted 2018-09-27T09:27:01.387

Reputation: 5 754

Related – Arnauld – 2018-09-27T10:02:48.230

1Despite the barrage of minor clarifications, nice challenge! – trichoplax – 2018-09-27T12:20:24.333

1@trichoplax I hope I have clarified the challenge a bit, I updated the Input section. – maxb – 2018-09-27T12:34:30.380

1Looks good. I've deleted all my comments that no longer apply – trichoplax – 2018-09-27T12:34:57.363

5Why doesn't "one" (pronounced "won") come after two and before zero? – Ben Miller - Remember Monica – 2018-09-27T18:50:58.197

3@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") – maxb – 2018-09-27T19:57:42.867

17So it is actually more "spelling sort" than "pronounciation sort" :-) – Paŭlo Ebermann – 2018-09-27T20:20:32.680

3bummer. I was hoping they would be sorted by how difficult they are to pronounce... – NH. – 2018-09-28T15:08:46.727

2This 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). – Janus Bahs Jacquet – 2018-09-29T14:09:47.800

@JanusBahsJacquet you could make another (more difficult) challenge where all numbers had to be sorted by spelling, or define an order for pronunciation. I chose an easier ordering for my challenge as I like to keep my challenges easy enough for everyone to understand and appreciate, while still being fun to golf. I agree that the name isn't perfect, but I chose it because I thought it'd gather some more attention for the challenge. – maxb – 2018-09-29T15:05:26.617

Do you have to take locale setting into account? Or is this limited to English spelling? – Ole Tange – 2018-09-30T12:50:35.003

@OleTange this challenge is limited to English only for simplicity and giving everyone an equal chance. I'm not a linguist, but in theory there could be a language where pronunciation order would be equal to regular sorting order, which gives a 1-byte solution to anyone using that locale – maxb – 2018-09-30T12:54:26.587

@maxb Sorry, I meant: Should it work for any locale? It will still give an equal chance to everyone, but you would have to support all known locales, which would make this task considerably harder. – Ole Tange – 2018-09-30T13:00:26.267

@OleTange oh, I understand now! That would be considerably harder indeed, but it is not required for this challenge. If you want to make your own challenge, you're very welcome to use this one as inspiration. – maxb – 2018-09-30T13:30:20.923

Answers

8

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

Emigna

Posted 2018-09-27T09:27:01.387

Reputation: 50 798

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

– Kevin Cruijssen – 2018-09-27T10:53:56.060

1@KevinCruijssen: That is weird. Yours would be 14 with …54-ì even – Emigna – 2018-09-27T11:35:07.467

@KevinCruijssen: You could do Σ•RT‹•Á…54-ìsSk for 15 – Emigna – 2018-09-27T11:41:22.303

•t∍ýJ•'-ìÁÁ would also work – Emigna – 2018-09-27T11:45:31.010

8

Haskell, 57 bytes

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

Try it online!

Vincent

Posted 2018-09-27T09:27:01.387

Reputation: 601

1This is so clean and readable! Definitely the winner in terms of ease of understanding. – Stephen Belden – 2018-10-02T18:05:14.023

8

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)

Jonathan Allan

Posted 2018-09-27T09:27:01.387

Reputation: 67 804

Even with the explanation, I don't feel very clever. Awesome solution! – maxb – 2018-09-27T20:00:50.537

The shorter version is probably also easier to understand! – Jonathan Allan – 2018-09-27T21:02:10.730

7

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.

nwellnhof

Posted 2018-09-27T09:27:01.387

Reputation: 10 037

6

K (ngn/k), 21 20 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

ngn

Posted 2018-09-27T09:27:01.387

Reputation: 11 449

6

JavaScript (SpiderMonkey), 69 bytes

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

Try it online!

Arnauld

Posted 2018-09-27T09:27:01.387

Reputation: 111 334

It looks like you can remove the +'', seeing as you're taking input as an array of strings. – Shaggy – 2018-09-27T10:01:18.267

@Shaggy I'm not taking strings anymore, as I'm not sure if that's allowed here. – Arnauld – 2018-09-27T10:01:58.610

Ah ... You're right. That's gonna add a couple of bytes to my solution. – Shaggy – 2018-09-27T10:03:47.703

6

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!

mypetlion

Posted 2018-09-27T09:27:01.387

Reputation: 702

1Could you remove the first 8 in the string? As Python returns -1 if the substring is not found. – maxb – 2018-09-27T16:11:46.383

@maxb Good catch. Edited. – mypetlion – 2018-09-27T16:46:51.077

2

Python 2 port is 58: lambda x:sorted(x,key=lambda y:map('54-9176320'.find,\y`))`

– Jonathan Allan – 2018-09-27T21:30:28.660

5

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

Sok

Posted 2018-09-27T09:27:01.387

Reputation: 5 592

4

Japt, 19 bytes

ñ_s ®n"54-9176320

Try it

Shaggy

Posted 2018-09-27T09:27:01.387

Reputation: 24 623

Nice solution! Input as a list of strings is not allowed unfortunately. – maxb – 2018-09-27T10:55:45.087

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)

– ETHproductions – 2018-09-28T02:58:56.477

Nice trick, @ETHproductions, thanks :) I'll have to remember that one for the future. – Shaggy – 2018-09-28T10:14:56.827

3

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.

Neil

Posted 2018-09-27T09:27:01.387

Reputation: 95 035

3

Ruby, 50 bytes

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

Try it online!

G B

Posted 2018-09-27T09:27:01.387

Reputation: 11 099

3

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

Olivier Grégoire

Posted 2018-09-27T09:27:01.387

Reputation: 10 647

1

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

– Kevin Cruijssen – 2018-09-27T16:58:16.410

3

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.

JayCe

Posted 2018-09-27T09:27:01.387

Reputation: 2 655

2

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.

Shieru Asakoto

Posted 2018-09-27T09:27:01.387

Reputation: 4 445

2

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 ]
]

Galen Ivanov

Posted 2018-09-27T09:27:01.387

Reputation: 13 815

2

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

underscore_d

Posted 2018-09-27T09:27:01.387

Reputation: 121

I see you also live by the motto "if I don't press enter there are fewer lines to debug" – maxb – 2018-09-30T06:30:11.093

1Hey, 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. – underscore_d – 2018-09-30T10:40:46.477

1

Hello! Squeezed your solution to 195 characters

– Max Yekhlakov – 2018-10-02T05:53:02.743

@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! – underscore_d – 2018-10-02T19:27:14.240

2

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.

LegionMammal978

Posted 2018-09-27T09:27:01.387

Reputation: 15 731

Always trust mathematica to have a combination of builtins. Clever solution! – maxb – 2018-09-30T10:55:54.470

1

05AB1E, 15 14 bytes

Σ•ĆU‘•…54-ìsSk

-1 byte thanks to @Emigna.

Try it online or verify all test cases.

Explanation:

Σ                 # Sort by:
 •ĆU‘•            #  Push the compressed integer 9176320
      …54-        #  Push the string "54-"
          ì       #  Prepend this string before the integer: "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 •ĆU‘• is 9176320.

Kevin Cruijssen

Posted 2018-09-27T09:27:01.387

Reputation: 67 575