The task

The credit rating agencies assign ratings to bonds according to the credit-worthiness of the issuer, and the "Big Three" credit rating agencies use a similar (though not identical) tiered rating system. These have a clear, logical order - using the S&P tiers, AAA > AA+ > AA > AA- > A+ > ... > BBB > B > ... > C. Moody's uses a similar system, but names their tiers differently (Aaa > Aa1 > Aa2 > ... > Baa1 > ... > C).

Your task is to design a program which takes as input a list of strings representing bond rating tiers and output the same list, sorted in descending order from the highest tier (AAA/Aaa) to the lowest tier (C).

Input / Output

You may choose the format of the input (list, one per argument, CSV file). You may assume that every item in the input list is a valid rating string and that all rating strings in a list came from the same rating agency. Additionally, you may assume that none of the funky NA-style ratings such as "NR" or "WR" will be included - this is strictly from "Aaa/AAA" to "C". There may be duplicate ratings in the input list, and if found they should not be removed.

You may also choose the format of the output as is appropriate for your language, with the only restriction being that it should be outputting some standard text encoding like UTF-8 or ASCII.

Rules and scoring

This is code golf, so lowest byte count wins, standard loopholes disallowed. Please specify what the input and output format is.

Example program and test cases

The example Python program below can be used as a standard example of the correct sort order. The lists Moody and SP are the orderings to use.

Moody = ['Aaa', 'Aa1', 'Aa2', 'Aa3', 'A1', 'A2', 'A3',
         'Baa1', 'Baa2', 'Baa3', 'Ba1', 'Ba2', 'Ba3',
         'B1', 'B2', 'B3', 'Caa', 'Ca', 'C']

SP = ['AAA', 'AA+', 'AA', 'AA-', 'A+', 'A', 'A-',
      'BBB+', 'BBB', 'BBB-', 'BB+', 'BB', 'BB-',
      'B+', 'B', 'B-', 'CCC', 'CC', 'C']

test_cases = [
    (['Aa2', 'Aaa', 'Aa1'], ['Aaa', 'Aa1', 'Aa2']),
    (['AA', 'AA-', 'AA+'], ['AA+', 'AA', 'AA-']),
    (['Baa1', 'Ba1', 'A1', 'B1', 'Aaa', 'C', 'Caa', 'Aa1'],
     ['Aaa', 'Aa1', 'A1', 'Baa1', 'Ba1', 'B1', 'Caa', 'C']),
    (['BBB+', 'BB+', 'A+', 'B+', 'AAA', 'C', 'CCC', 'AA+'],
     ['AAA', 'AA+', 'A+', 'BBB+', 'BB+', 'B+', 'CCC', 'C']),
    (['B3', 'B1', 'B2'], ['B1', 'B2', 'B3']),
    (['B-', 'B+', 'B'], ['B+', 'B', 'B-']),
    (['B3', 'Caa', 'Aa1', 'Caa', 'Ca', 'B3'],
     ['Aa1', 'B3', 'B3', 'Caa', 'Caa', 'Ca']),
    (['B-', 'CCC', 'AA+', 'CCC', 'CC', 'B-'],
     ['AA+', 'B-', 'B-', 'CCC', 'CCC', 'CC'])

mdy_sort = lambda x: Moody.index(x)
sp_sort = lambda x: SP.index(x)

for l_in, l_out in test_cases:
    sort_key = mdy_sort if set(l_in).issubset(set(Moody)) else sp_sort
    assert sorted(l_in, key=sort_key) == l_out

Test cases

In case the python-style test case formatting is inconvenient, I've output it as space-delimited input strings (grouped in two-line pairs input followed by output):

Aa2 Aaa Aa1
Aaa Aa1 Aa2


Baa1 Ba1 A1 B1 Aaa C Caa Aa1
Aaa Aa1 A1 Baa1 Ba1 B1 Caa C


B3 B1 B2
B1 B2 B3

B- B+ B
B+ B B-

B3 Caa Aa1 Caa Ca B3
Aa1 B3 B3 Caa Caa Ca


Note: I mention the "Big Three" but only specify Moody's and S&P here - the reason is that the third, Fitch, uses the same system as S&P when you don't take into account the NA-style ratings, so including Fitch would be redundant.

  • 3
    \$\begingroup\$ Dude, if this is your first post, I am damn well impressed. +1 \$\endgroup\$ – Addison Crump Feb 10 '16 at 23:10
  • 9
    \$\begingroup\$ @VoteToClose Long time listener, first time caller. ;) \$\endgroup\$ – Paul Feb 10 '16 at 23:11
  • 1
    \$\begingroup\$ Should the output be case sensitive? \$\endgroup\$ – andlrc Feb 10 '16 at 23:27
  • \$\begingroup\$ @dev-null Yes, the output should be the same input strings, reordered. \$\endgroup\$ – Paul Feb 10 '16 at 23:29
  • 1
    \$\begingroup\$ Can the input lists contain any equal ratings? If so should all equals be output, or remove duplicates? \$\endgroup\$ – Digital Trauma Feb 11 '16 at 0:09

Pyth, 16 bytes


We sort lexicographically by a key using @Neil's approach. Input and output are as lists; this does not mutate the list.

o+Xs}RGrN0N\z\,Q       Implicit: Q = input list
                        lambda N  (N is a bond rating)
       rN0               Convert N to lowercase
    }RG                  Map is-in G, the lowercase alphabet.
   s                     Sum the list of bools; the number of letters in N.
  X       N\z            Insert "z" at that position in N.
 +           \,          Append "," to the end.
                         This works because "," is between "+" and "-" lexicographically.
o              Q       Sort Q, using that lambda as a key.

Try it here. Test cases are all bond ratings of each rating scheme, with a duplicate thrown in.

| improve this answer | |

ES6, 71 65 bytes


By inserting a z after the letters and suffixing a , we just have to sort the strings lexically.

Edit: Saved 6 bytes thanks to @user81655.

| improve this answer | |
  • \$\begingroup\$ Nice idea. It could also be a bit shorter by defining a separate function for the replace and using $& inside it: a=>a.sort((b,c)=>(r=t=>t.replace(/[^A-z]*$/,"z$&,"))(b)>r(c)||-1) \$\endgroup\$ – user81655 Feb 11 '16 at 10:17
  • \$\begingroup\$ @user81655 Bah, I originally had s="$1z$2," and although I realised that I could golf away the $1 it hadn't occurred to me that I could now golf away the $2 too... \$\endgroup\$ – Neil Feb 11 '16 at 19:37

Bash + GNU utilities, 45

Credit is due to @Neil for the approach.

sed s/$/e/|tr +-3 d-l|sort|tr -d e|tr d-l +-3

In my locale sort order, numbers sort before letters and - sorts before +. So these characters are transliterated into the alphabet range so they sort in the right order.

Try it online.

| improve this answer | |

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.