7
\$\begingroup\$

Related: Sort these bond ratings

Credit rating agencies assign ratings to bonds according to the credit-worthiness of the issuer. The rating business is mostly controlled by "Big Three" credit rating agencies (i.e. Fitch Ratings, Moody's, and Standard & Poor's (S&P)), which use a similar tiered rating system.

Task

For this challenge, we will consider only long-term non in-default tiers of two rating formats, Moody's and Fitch/S&P. NA-style ratings such as NR or WR will not be included.

Your task is to design a program or function that takes as input a rating tier in one of the two format and outputs the equivalent tier in the other format. Your program must support both formats in input and output, i.e. your program cannot be able only to convert from Moody's to Fitch/S&P or vice versa.

Edit: your program may output trailing whitepaces.

The following table shows the tier pairs.

Moody's Fitch/S&P
------- ---------
Aaa     AAA
Aa1     AA+
Aa2     AA 
Aa3     AA-
A1      A+ 
A2      A  
A3      A- 
Baa1    BBB+
Baa2    BBB
Baa3    BBB-
Ba1     BB+
Ba2     BB 
Ba3     BB-
B1      B+ 
B2      B  
B3      B- 
Caa     CCC
Ca      CC 
Ca      C  

Notes

  • there are no tiers in the two formats that are identical;
  • for each rating tier (except Ca) there is one (and only one) equivalent tier in the other format;
  • Ca is equivalent to either CC or C, depending on the specific case;
    • when your program takes as input Ca, it must output either CC or C indifferently;
    • your program must output Ca when the input is CC or C.

Rules and scoring

This is code golf, so lowest byte count wins, standard loopholes disallowed.

\$\endgroup\$
5
  • 4
    \$\begingroup\$ What do you mean by "indifferently"? Do you mean that we may output either CC or C when given Ca? \$\endgroup\$ Dec 23, 2023 at 17:35
  • 1
    \$\begingroup\$ May we output any trailing whitespace? \$\endgroup\$
    – Tbw
    Dec 23, 2023 at 18:16
  • 3
    \$\begingroup\$ Most (but not all) of your minus signs are not the ASCII minus sign. May we use the ASCII one? \$\endgroup\$ Dec 23, 2023 at 18:18
  • 2
    \$\begingroup\$ @JonathanAllan regarding CC or C, yes; about the minus sign: yes, it was a typo, fortunately others have assumed it's ok the use - \$\endgroup\$
    – matteo_c
    Dec 24, 2023 at 21:40
  • \$\begingroup\$ @Tbw yes, you can output trailing whitespaces \$\endgroup\$
    – matteo_c
    Dec 24, 2023 at 21:44

11 Answers 11

5
\$\begingroup\$

Jelly, 36 bytes

Assumes that we may use ASCII only input (so - rather than the similarly looking used in the post) and that "indifferently" is being used to mean either/or (here Ca produces CC only).

;⁼¡“C”Ḣð;“13-+a”,Ṛ$y⁹⁸;;ḟẇ“¶®O^»$?”2

A monadic Link that accepts a list of characters (one of the ratings) and outputs the equivalent rating from the other agency.

Try it online!

How?

;⁼¡“C”Ḣð;“13-+a”,Ṛ$y⁹⁸;;ḟẇ“¶®O^»$?”2 - Link: list of characters, R
   “C”                               - set the right argument to ['C']
  ¡                                  - repeat...
 ⁼                                   - ...times: is {R} equal to {['C']}? 
;                                    - ...action: {R} concatenate {['C']}
                                         -> R="C" becomes "CC" others unaffected
      Ḣ                              - head
       ð                             - start a new dyadic chain - f(Head, say 'H', Rest)
        ;“13-+a”                     - {Head} concatenate "13-+a"
                ,Ṛ$                  - pair with its reverse -> ["H13-+a","a+-31H"]
                   y⁹                - translate {Rest} using {that mapping}
                     ⁸;              - left arg {=Head} concatenate {translated Rest}
                                  ”2 - set the right argument to '2'
                                 ?   - if...
                         ẇ“¶®O^»$    - ...condition: is sublist of "AaBaa"?
                       ;             - ...then: concatenate {'2'}
                        ḟ            - ...else: filter discard {'2'}
\$\endgroup\$
5
\$\begingroup\$

Google Sheets, 168 bytes

iferror(find()) by JvdV, 168 bytes:

=let(b,left(A1),c,right(A1),f,len(split(A1,"123+-")),iferror(rept(b,f)&mid("+-",find(c,"132a"),1),b&rept("a",f-1+(A1="C"))&rept(mid(1223,find(c,"+AB-C"),1),A1<>"AAA")))

Brute force hlookup(), 217 bytes:

=let(m,split("Aaa0Aa10Aa20Aa30A10A20A30Baa10Baa20Baa30Ba10Ba20Ba30B10B20B30Caa0Ca0Ca",0),f,split("AAA0AA+0AA0AA-0A+0A0A-0BBB+0BBB0BBB-0BB+0BB0BB-0B+0B0B-0CCC0CC0C",0),ifna(hlookup(A1,{m;f},2,0),hlookup(A1,{f;m},2,0)))

regexextract() and regexmatch(), 221 bytes:

=let(a,regexextract(A1,"[A-c]+"),c,left(a)&rept("a",len(a)-1)&switch(right(A1),"+","1","-","3",),if(regexmatch(A1,"[a-c\d]"),rept(left(a),len(a))&switch(right(A1),"1","+","3","-",),c&if(regexmatch(c,"^(Aa?|Ba?a?)$"),2,)))

Convoluted arrays of regexes applied two ways, 298 bytes:

=let(r,lambda(a,b,reduce(A1,sequence(rows(a)),lambda(t,i,regexreplace(t,index(a,i),index(b,i))))),a,{"Baa";"Ba";"Caa";"Ca";"1";"2";"3";"$12";"Ca"},b,{"BBB";"BB";"CCC";"^C$";"\+";"ø";"-";"^(AA?|Ba?a?)$";"^CC$"},regexreplace(if(regexmatch(A1,"[a-c\d]"),upper(r(a,b)),proper(r(b,a))),"\\|Ø|\^|\$",""))

Put the rating in cell A1 and the formula in cell B1.

\$\endgroup\$
3
  • 1
    \$\begingroup\$ You could for 175 bytes use =LET(a,A1,b,LEFT(a),c,RIGHT(a),d,"a",e,SPLIT(a,"123+-"),f,LEN(e),IFERROR(REPT(b,f)&MID("+-",FIND(c,132&d),1),b&REPT(d,f-1+(a="C"))&REPT(MID(1223,FIND(c,"+AB-C"),1),a<>"AAA"))) which is a port from my Excel answer, but the GS SPLIT() function is just a bit cleaner which saves more bytes. \$\endgroup\$
    – JvdV
    Dec 24, 2023 at 22:37
  • 1
    \$\begingroup\$ @JvdV thanks, added that. It's probably the best approach. \$\endgroup\$ Dec 24, 2023 at 22:50
  • 1
    \$\begingroup\$ Yeah no worries. And the combination of LEN(SPLIT()) is something I totally missed so thanks for that =) \$\endgroup\$
    – JvdV
    Dec 24, 2023 at 22:54
3
\$\begingroup\$

JavaScript (ES6), 91 bytes

-1 thanks to @l4m2
-4 thanks to @tsh

The output may contain a trailing space.

([o,...a],s=o+` +-a213`)=>"C"+a.map(c=>o+=s[s.indexOf(c)^4])<s?"Ca":/^A?$|B$/.test(a)?o+2:o

Try it online!

Commented

( [o,            // o = 1st character of the input
      ...a],     // a[] = remaining characters
  s = o +        // s is a lookup string used to transpose:
      ` +-a213`  //   "a" <-> first character
                 //   "+" <-> "1"
                 //   " " <-> "2"
                 //   "-" <-> "3"
) =>             //
"C" +            // prefix the result of the map() with "C"
a.map(c =>       // for each character c in a[]:
  o += s[        //   append to o ...
    s.indexOf(c) //   ... the character corresponding to
    ^ 4          //   the transposition of c
  ]              //
)                // end of map()
< s ?            // if o is "C":
  "Ca"           //   return "Ca"
:                // else:
  /^A?$|B$/      //   if the input was any of "A", "AA", "B",
  .test(a) ?     //   "BB" or "BBB":
    o + 2        //     append a "2"
  :              //   else:
    o            //     leave o unchanged
\$\endgroup\$
2
  • 1
    \$\begingroup\$ 95 \$\endgroup\$
    – l4m2
    Dec 25, 2023 at 9:05
  • 1
    \$\begingroup\$ /^A?$|B$/.test(a)?o+2:o \$\endgroup\$
    – tsh
    Dec 26, 2023 at 4:13
2
\$\begingroup\$

Retina 0.8.2, 59 bytes

^C$
CC
(?<!AA)[AB]$
$&,
1>T`321+--aL`-_+1-3#a`.
+`(.)#
$1$1

Try it online! Link is to test suite that does some magic splitting and joining. Converts Ca to CC. Explanation:

^C$
CC

Change C to CC.

(?<!AA)[AB]$
$&,

Except for AAA, append a , to any string ending with A or B. The choice of , is important for the transliteration as it is between + and - in ASCII.

1>T`321+--aL`-_+1-3#a`.

Excluding the first character, transliterate 3 to -, delete 2, transliterate 1 to +, + to - to 1 to 3, a to # and all uppercase letters to a. (Having the - be first in the target pattern makes it a literal -. I couldn't do that in both patterns but fortunately in the source pattern I could use a range from + to -.)

+`(.)#
$1$1

Make #s copy the preceding letter.

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

Python, 171 bytes

dict(zip(a:="Aaa Aa1 Aa2 Aa3 A1 A2 A3 Baa1 Baa2 Baa3 Ba1 Ba2 Ba3 B1 B2 B3 Caa Ca Ca C CC CCC B- B B+ BB- BB BB+ BBB- BBB BBB+ A- A A+ AA- AA AA+ AAA".split(),a[::-1])).get

Try it online!

Boring hard-code Python answer is shorter than current ones...

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

Charcoal, 38 bytes

⭆×⁻θ2⊕⁼θC⎇κ§⁺§θ⁰13-+a⌕a+-31ιι¿№AABBBθ2

Try it online! Link is to verbose version of code. Explanation: Port of @JonathanAllan's Jelly answer.

⭆×⁻θ2⊕⁼θC⎇κ§⁺§θ⁰13-+a⌕a+-31ιι

Remove any 2 from the input, double it if it's C, and then transliterate all characters after the first from a+-31 to #13-+a, where # is the first character of the input; characters in the input that don't match the source string are replaced with the last character of the destination string.

¿№AABBBθ2

Append a 2 if the input is a substring of AABBB.

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

Python 3, 200 bytes

def f(x):
 r=str.replace;d=(6,0)[(x*2)[1]in'a123'];y=x
 for i in 0,1,2:y=r(y,chr(49+i-d),chr(43+i+d))
 y=r(y,',','')*((x=='C')+1)+'2'[not d*(x in'ABBBAA'):]
 return x[0]+r(y[1:],*('a',x[0])[::1-d//3])

Try it online!

Probably can be optimized, but it's the best I could come up with.

r=str.replace
# Set an alias for `str.replace`, we'll use it three times

d=(6,0)[(x*2)[1]in'a123']
# Detects if `x` is on Moody's format by checking the 2nd character.
# If positive, set `d` to 0, otherwise set `d` to 6.
# The multiplication by 2 is to handle single-character strings.

y=x
# Create a variable for the following replacements:

for i in 0,1,2:y=r(y,chr(49+i-d),chr(43+i+d))
# If `d` is 0, it will replace all '123' occurrences with '+,-', respectively
# If `d` is 6, it will replace all '+,-' occurrences with '123', respectively

y=r(y,',','')
# An annoying comma will appear in the first conversion, so remove it
y=y*((x=='C')+1)
# Handle the special case where `x` is equal to 'C', duplicating it
y=y+'2'[not d*(x in'ABBBAA'):]
# If `x` is on Fitch/S&P format, add a trailing '2' to the 
# result if `x` is either 'A', 'AA', 'B', 'BB' or 'BBB'

return x[0]+r(y[1:],*('a',x[0])[::1-d//3])
# Since the first character of the input string will not change,
# keep it. For the remaining characters, if `x` is on Moody's 
# format, replace all 'a's with the first character. Otherwise,
# replace all occurrences of the first character with 'a's.
\$\endgroup\$
1
\$\begingroup\$

Excel ms365, 207179 bytes

Thanks to @doubleunary's idea for nesting TEXTSPLIT() inside LEN().

Assuming input in A1:

=LET(a,A1,b,LEFT(a),c,RIGHT(a),e,LEN(@TEXTSPLIT(a,{1,2,3,"+","-"})),IFERROR(REPT(b,e)&MID("+-",FIND(c,"132a"),1),b&REPT("a",e-1+(a="C"))&IF(a="AAA",,MID(1223,FIND(c,"+AB-C"),1))))
\$\endgroup\$
1
\$\begingroup\$

Python 3, 235 bytes

eval('lambda x:(x?a",x[0])?1","+")?2","")?3","-")if"a"in x or x[-1]in"123"else x[0]+x[1:]?+","1")?-","3")?B","a")?C","a")?A","a")+("2"if x[-1]not in"+-"else""))?Aaa2","Aaa")?C2","Ca")?Ca2","Ca")?Caa2","Caa")'.replace('?','.replace("'))

Try it online!

Returns an anonymous function.

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

C (gcc), 285 275 248 bytes

-10 bytes by moving strcmp inside for condition and using "incorrect" pointer types (thanks @ceilingcat)

-27 by assuming 64-bit little-endian long

i;*t="Aaa AAA Aa1 AA+ Aa2 AA  Aa3 AA- A1  A+  A2  A   A3  A-  Baa1BBB+Baa2BBB Baa3BBB-Ba1 BB+ Ba2 BB  Ba3 BB- B1  B+  B2  B   B3  B-  Caa CCC Ca  CC  Ca  C   ";f(int*s){long j=538976288;memcpy(&j,s,strlen(s));for(i=38;t[--i]-j;);j=t[i^1];puts(&j);}

Try it online!

\$\endgroup\$
1
0
\$\begingroup\$

Python 3 (256)

I know its not very short or optimized, but here is my first attempt at code golf! Please let me know if you have a tip for me to improve!

f=lambda r:(s:=r.replace("1","+").replace("3","-"))[0]*len(s.strip("+-2"))+s[-1].strip("2a")if "a" in r or r[-1]in[1,2,3] else r[0]+"a"*(l:=len(r.strip("+-"))-1)+(""if r[0]=="C"or r=="A"*3 else str(["+"," ","-"].index((r+" ")[l+1])+1))+("a"if r=="C"else"")

I do see that it is longer than the hardcoded python option, so I'll definitely have to try to beat that at the very least. Also comment if you want an explanation.

Edit: Here is another method using regex that turned out to unfortunately be longer (268) with the import

import re
t=str.maketrans("+-13","13+-")
f=lambda r: (r.replace("a",r[0]).translate(t).strip("2"))if r[-1]in"123"or"a"in r else((o:=re.sub("(Aa?|Ba*)$",lambda m:m.group()+"2",re.sub("(?<=\\w)\\w+",lambda m:"a"*len(m.group()),r)).translate(t))+("a"if len(o)<2 else""))
\$\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.