49
\$\begingroup\$

Given one of the 50 U.S. state names on the left as input, output its two-letter postal code as shown to the right:

Alabama         AL
Alaska          AK
Arizona         AZ
Arkansas        AR
California      CA
Colorado        CO
Connecticut     CT
Delaware        DE
Florida         FL
Georgia         GA
Hawaii          HI
Idaho           ID
Illinois        IL
Indiana         IN
Iowa            IA
Kansas          KS
Kentucky        KY
Louisiana       LA
Maine           ME
Maryland        MD
Massachusetts   MA
Michigan        MI
Minnesota       MN
Mississippi     MS
Missouri        MO
Montana         MT
Nebraska        NE
Nevada          NV
New Hampshire   NH
New Jersey      NJ
New Mexico      NM
New York        NY
North Carolina  NC
North Dakota    ND
Ohio            OH
Oklahoma        OK
Oregon          OR
Pennsylvania    PA
Rhode Island    RI
South Carolina  SC
South Dakota    SD
Tennessee       TN
Texas           TX
Utah            UT
Vermont         VT
Virginia        VA
Washington      WA
West Virginia   WV
Wisconsin       WI
Wyoming         WY

Rules

  • Input and output are both case sensitive. You many not output e.g. Al for Alabama.
  • You may assume the input is one of the 50 state names shown above.
  • You may not access the internet or use built-in state data (looking at you, Mathematica).

Separate lists of inputs and outputs can be found in this snippet (please don't run it, it's just for compressing the post):

Alabama
Alaska
Arizona
Arkansas
California
Colorado
Connecticut
Delaware
Florida
Georgia
Hawaii
Idaho
Illinois
Indiana
Iowa
Kansas
Kentucky
Louisiana
Maine
Maryland
Massachusetts
Michigan
Minnesota
Mississippi
Missouri
Montana
Nebraska
Nevada
New Hampshire
New Jersey
New Mexico
New York
North Carolina
North Dakota
Ohio
Oklahoma
Oregon
Pennsylvania
Rhode Island
South Carolina
South Dakota
Tennessee
Texas
Utah
Vermont
Virginia
Washington
West Virginia
Wisconsin
Wyoming

AL
AK
AZ
AR
CA
CO
CT
DE
FL
GA
HI
ID
IL
IN
IA
KS
KY
LA
ME
MD
MA
MI
MN
MS
MO
MT
NE
NV
NH
NJ
NM
NY
NC
ND
OH
OK
OR
PA
RI
SC
SD
TN
TX
UT
VT
VA
WA
WV
WI
WY

(Non-scoring) Brownie points if you can also take District of Columbia as input and produce DC, Virgin Islands, etc etc.

Scoring

This is , so the shortest code in bytes in each language wins.

(Originally proposed by ETHProductions)

\$\endgroup\$
  • 11
    \$\begingroup\$ For those curious to know Mathematica's approach: Entity[a="AdministrativeDivision",{#,"UnitedStates"}]@EntityProperty[a,"StateAbbreviation"]& \$\endgroup\$ – DavidC May 26 '17 at 7:05
  • 12
    \$\begingroup\$ @BetaDecay Questions that are closed as off-topic are not useful dupe targets. \$\endgroup\$ – Mego May 26 '17 at 7:08
  • 7
    \$\begingroup\$ @DavidC You can save 20 bytes: Entity["AdministrativeDivision",{#,"UnitedStates"}]@"StateAbbreviation"& :) \$\endgroup\$ – ngenisis May 26 '17 at 18:28
  • 2
    \$\begingroup\$ Offer extra credit for including the other 12 two-character codes in the complete official list of US postal abbreviations: AA (ARMED FORCES AMERICAS), AE (ARMED FORCES EUROPE), AP (ARMED FORCES PACIFIC), AS (AMERICAN SAMOA), DC (DISTRICT OF COLUMBIA), FM (FEDERATED STATES OF MICRONESIA), GU (GUAM), MH (MARSHALL ISLANDS), MP (NORTHERN MARIANA ISLANDS), PR (PUERTO RICO), PW (PALAU), VI (VIRGIN ISLANDS). \$\endgroup\$ – joe snyder May 27 '17 at 15:47
  • 2
    \$\begingroup\$ Yeah this is not a dupe. \$\endgroup\$ – Christopher May 27 '17 at 18:27

21 Answers 21

25
\$\begingroup\$

Javascript, 124 117 bytes

(saved 7 bytes thanks to hvd)

x=>/.+[A-Z]|A..[sz]k?|M.ss.s?|M[io]n?t?|Nev?|[AFIOUW][^o]|T..|.+/.exec(x)[0].replace(/(.).*(.)/,'$1$2').toUpperCase()

Explanation:

  • The regexp finds a match with the first and last letters the two letters of the abbreviation
  • First part matches states with more than two words (including District of Columbia)
  • Second part matches Alaska and Arizona
  • Third part matches Massachusets, Mississippi and Missouri
  • Fourth part matches Michigan, Minnesota and Montana
  • Fifth part matches Nebraska and Nevada
  • Sixth part matches all remaining states abbreviated to their first two letters, with a special case to exclude Iowa
  • Seventh part matches all remaining states abbreviated to their first and third letters
  • Eighth part matches everything else, which are abbreviated to their first and last letters
  • Then it is just a case of stripping of those letters and capitalising
  • Also matches Puerto Rico and American Samoa, but not Guam, Marianas islands or US Virgin Islands
\$\endgroup\$
  • \$\begingroup\$ Wow, this is great! +1! \$\endgroup\$ – NoOneIsHere May 26 '17 at 15:00
  • 3
    \$\begingroup\$ Nice! Some more opportunities: the initial [A-Z] is not necessary since the input is known to be valid. The Iowa special case can be shortened to [AFIOUW][^o] to exclude it, leaving it for the final .+. \$\endgroup\$ – hvd May 26 '17 at 15:23
  • 1
    \$\begingroup\$ Your regex machinery is so efficient compared to mine...wish there was a way to make it work with my shorter regex. But they're constructed on such different principles. \$\endgroup\$ – Steve Bennett May 26 '17 at 15:36
  • 1
    \$\begingroup\$ Well there are quite a few that are weird in their own ways. There's a nice collection that match both the "first and last" and "first two" rules (Colorado, Delaware, California...), but then Mississippi (MS) ruins it. \$\endgroup\$ – Steve Bennett May 26 '17 at 16:26
  • 1
    \$\begingroup\$ 101: s=>s[0]+/.+[A-Zz]|Nev?|.*sk|M.ss.s?|M[io]n?t?|[AFIOUW][^o]|Te.|.+/.exec(s)[0].slice(-1).toUpperCase() Can we get to 100? :) \$\endgroup\$ – Steve Bennett May 27 '17 at 23:12
22
\$\begingroup\$

Javascript, 137 135 134 132 113 110 108 101 99 94 93 92 bytes

This is based on HP Williams solution, with some improvements outside the regex, and a couple of tweaks within it.

s=>s[0]+
/.*( .|z)|...s.s?|T..|M[i-t]+|[AFINOUW][^o]v?|.*/
.exec(s)[0].slice(-1).toUpperCase()

(Linebreaks for readability only)

Commentary for the regex:

.*( .|z)|      // Two-or-three word states, plus Arizona
...s.s?|       // Mississippi, Missouri, Massachussetts, Alaska, and (non-harmfully) Kansas
M[i-t]+|       // Montana, Minnesota, Michigan
[AFINOUW][^o]v?|  // All the other first-two-letter states, avoiding Iowa, plus Nevada
T‌​..|           // Tennessee, Texas
.+             // Everything else is first-and-last

Pointless alternative regex (same length):

/...(a$|z|s.s?|.* .)|M[i-t]+|T..|[AFINOUW].v?|.*/  

History

94

s=>s[0]+/.*( .|z)|...s.s?|M[io]n?t?|[AFIOUWN][^o]v?|T..|.*/
.exec(s)[0].slice(-1).toUpperCase()

99

s=>s[0]+/.*( .|z|l.*k)|T..|M.ss.s?|M[io]n?t?|[AFIOUWN][^o]v?|.*/
.exec(s)[0].slice(-1).toUpperCase()

101

s=>s[0]+/.+[A-Zz]|Nev?|.*sk|M.ss.s?|M[io]n?t?|[AFIOUW][^o]|T‌​e.|.+/
.exec(s)[0].sl‌​ice(-1).toUpperCase(‌​)

108

 s=>s[0]+/MI(N|SSO|S)|[CDGHKLPV].*|.* .|.*[XZV]|.*?N[NTE]|.*(SK|D$|WA)|../
.exec(s.toUpperCase())[0].slice(-1)

110

s=>s[0]+/MI(N|SSO|S)|[CGHKLPV].*|.* .|.*[XZV]|.*?N[NTE]|.*(SK|[ED]$|WA)|../
.exec(s.toUpperCase())[0].slice(-1)

113

s=>s[0]+/^MI(N|SSO|S)|^[CGHKLPV].*|.*?( .|[XZV])|.*?N[NTE]|.*(SK|[ED]$|WA)|../
.exec(s.toUpperCase())[0].slice(-1)

132

s=>(S=s.toUpperCase(),' ._SSO_^MI[NS]_^[CGHKLPV].*_.V_N[TNE]_SK_[ED]$_WA_Z_X_..'
.split`_`.some(p=>s=S.match(p)),S[0]+s[0].slice(-1))

134

s=>' ._SSO_^MI[NS]_^[CGHKLPV].*_.V_N[TNE]_SK_E$_D$_WA_Z_X_..'.split`_`
.map(p=>s=(z=s.toUpperCase().match(p))?s[0]+z[0].slice(-1):s)&&s

135

s=>' ._SSO_^MI[NS]_LASK_^[CGHKLPV].*_NT_EN_[DE]$_WA_.[XVZ]_..'.split`_`
.map(p=>s=(z=s.toUpperCase().match(p))?s[0]+z[0].slice(-1):s)&&s

137

s=>' ._OWA_SSO_ONT_^MI[NS]_LASK_^[CGHKLPV].*_EN_[DE]$_.[XVZ]_..'.split`_`.
map(p=>s=(z=s.toUpperCase().match(p))?s[0]+z[0].slice(-1):s)&&s
\$\endgroup\$
  • \$\begingroup\$ This is just insane, I wish I could upvote again. \$\endgroup\$ – ETHproductions Jun 1 '17 at 0:35
  • \$\begingroup\$ Don't look now! 99! \$\endgroup\$ – Steve Bennett Jun 1 '17 at 2:59
  • \$\begingroup\$ 94. Totally crazy. I discovered this optimisation (...s.s? captures Mississippi, Missouri, Massachussetts and Alaska) by accident. I was actually in the process of moving the .*sk fragment to somewhere else, and the tests just happen to ran and passed, while the fragment wasn't present anywhere. Easiest 5 character gain ever! \$\endgroup\$ – Steve Bennett Jun 1 '17 at 5:22
  • \$\begingroup\$ Wow, and I just realised that ...s.s? is also inadvertently matching Kansas, and is, by miracle, still giving the right result. \$\endgroup\$ – Steve Bennett Jun 1 '17 at 5:31
  • \$\begingroup\$ And one more character with M[onti]+. So weird: if the state starts with M, then the second letter is the last in a sequence of at least one o, n, t, or i. A very weird way to capture Michican, Minnesota, or Montana. \$\endgroup\$ – Steve Bennett Jun 1 '17 at 6:35
20
\$\begingroup\$

JavaScript (ES6), 156 136 bytes

s=>s[0]+'.KT..N.VEA.XVL.H.TZ.AA..I.EADATO.JTISRDIYOI.DALA.Y.KE.C.D.R.YAS.NM...C.L...N'[parseInt(s.split` `.join``,36)%359%248*8%99*3%83]

Demo

let f =

s=>s[0]+'.KT..N.VEA.XVL.H.TZ.AA..I.EADATO.JTISRDIYOI.DALA.Y.KE.C.D.R.YAS.NM...C.L...N'[parseInt(s.split` `.join``,36)%359%248*8%99*3%83]

;[
  "Alabama", "Alaska", "Arizona", "Arkansas", "California", "Colorado", "Connecticut", "Delaware", "Florida", "Georgia", "Hawaii", "Idaho",
  "Illinois", "Indiana", "Iowa", "Kansas", "Kentucky", "Louisiana", "Maine", "Maryland", "Massachusetts", "Michigan", "Minnesota", "Mississippi",
  "Missouri", "Montana", "Nebraska", "Nevada", "New Hampshire", "New Jersey", "New Mexico", "New York", "North Carolina", "North Dakota", "Ohio",
  "Oklahoma", "Oregon", "Pennsylvania", "Rhode Island", "South Carolina", "South Dakota", "Tennessee", "Texas", "Utah", "Vermont", "Virginia",
  "Washington", "West Virginia", "Wisconsin", "Wyoming"
]
.map(s => console.log(s, '-->', f(s)))

\$\endgroup\$
  • 1
    \$\begingroup\$ Is this bruteforced to be optimal using this method? \$\endgroup\$ – ASCII-only May 26 '17 at 8:10
  • 2
    \$\begingroup\$ @ASCII-only This is bruteforced on arbitral ranges, so it's only guaranteed to be optimal for X MOD[50-1000] MOD[50-1000] MOD[50-100]. But the .slice(1) was a mistake. Currently running again on the whole string. \$\endgroup\$ – Arnauld May 26 '17 at 8:13
  • 2
    \$\begingroup\$ Could you explain to a newb what this does? \$\endgroup\$ – Hankrecords May 26 '17 at 10:17
  • 1
    \$\begingroup\$ @Hankrecords Sure, will do. (But I'm in a train with limited Internet access right now.) \$\endgroup\$ – Arnauld May 26 '17 at 11:12
  • 1
    \$\begingroup\$ now mine is 135! \$\endgroup\$ – Steve Bennett May 26 '17 at 14:30
17
\$\begingroup\$

Jelly, 75 bytes

³Oḅ⁹%⁽qġ%14ị⁽"wB¤Ḥ
“¿ØƈṢḍw÷S=)µZṂ÷ĿæÆɱ»Ḳiµ%2+3¢⁸?
e“\B“½ƈN»ȧ5ȯÇ’
i⁶ȯÇ‘ịṭḢŒu

Try it online!

...or see a test suite - Note a small change had to be made to run the full program for multiple inputs in one go (I swapped out the program input atom, ³, for a recall from register, ®, one and set the register to each state name in turn).

How?

Calculates the index to use for the second character of the postal code, appends it to the first character and uppercases the result.

First finds the index of a space character (or 0 if not found);

Else checks if it is Alaska or Missouri (yielding 5 for k or o);

Else finds the index of the input state in the list Arizona Minnesota Mississippi Nevada Montana Texas Tennessee (or 0 if not found) - if so it takes that index mod 2 plus 3 (for z n s v t x n);

Else converts the string to ordinals, converts that from base 256, finds the remainder of that after dividing by 29487, finds the remainder of that after dividing by 14 and uses that to index into the binary representation of 9620 and doubles the result - yielding 0 for states that use their final letter and 2 for those that use their second letter.

All but the very first case are incremented, and the resulting value is decremented (upping the space index found by one).

i⁶ȯÇ‘ịṭḢŒu - Main link: state string
 ⁶         - space character
i          - first index (of a space character in the state) or 0 if not found  (n-1 or 0)
   Ç       - call link 3 as a monad  (get n-1 in other cases)
  ȯ        - logical or  (yielding n-1)
    ‘      - increment   (yielding n)
     ị     - index into the state string (get the nth character)
       Ḣ   - head the state string (get the first character)
      ṭ    - tack
        Œu - convert to uppercase
           - implicit print

e“\B“½ƈN»ȧ5ȯÇ’ - Link 3: n-5 or ... : state string
 “\B“½ƈN»      - ["Alaska","Missouri"]
e              - exists in? (1 if so, 0 if not)
          5    - 5
         ȧ     - logical and
            Ç  - call link 2 as a monad
           ȯ   - logical or
             ’ - decrement

“¿ØƈṢḍw÷S=)µZṂ÷ĿæÆɱ»Ḳiµ%2+3¢⁸? - Link 2: n = 3 or n = 4 or ... : state string
“¿ØƈṢḍw÷S=)µZṂ÷ĿæÆɱ»           - "Arizona Minnesota Mississippi Nevada Montana Texas Tennessee"
                    Ḳ          - split at spaces
                     i         - first index of state string in that list or 0
                      µ        - monadic chain separation (call that i)
                             ? - if: 
                            ⁸  -   link's left argument, i
                               - then:
                       %2      -   mod 2
                         +3    -   plus 3  - odd entries to 4: AriZona, MisSissippi, MonTana, TenNessee
                               -            even entries to 3: MiNnesota, NeVada, TeXas
                               - else:
                           ¢   -   call link 1 as a nilad

³Oḅ⁹%⁽qġ%14ị⁽"wB¤Ḥ - Link 1 ...n=2 or n=0: no arguments
³                  - program's 1st input    e.g. Iowa          or Ohio
 O                 - cast to ordinals            [73,111,119,97]  [79, 104, 105, 111]
   ⁹               - 256
  ḅ                - convert from base           1232041825       1332242799
     ⁽qġ           - 29487
    %              - modulo                      15991            20139
        %14        - modulo 14                   3                7
                ¤  - nilad followed by link(s) as a nilad:
            ⁽"w    -   9620                     V       V
               B   -   convert to binary = [1,0,0,1,0,1,1,0,0,1,0,1,0,0]
           ị       - index into                  0                1
                 Ḥ - double                      0                2
                   -   ...0th index of Iowa is 'a', 2nd of Ohio is 'h'
\$\endgroup\$
  • 1
    \$\begingroup\$ This is the longest Jelly I've ever seen =) \$\endgroup\$ – caird coinheringaahing May 27 '17 at 8:13
11
\$\begingroup\$

Python 2, 191 bytes

lambda s:s[0]+("KZD"*5+"HNTD"*5+"AYY"*4+"__L_O_VTA_I__A_T_RS_KA__S_"+"MOO"*5+"I_C_"+"AE"*6+"_I_D__A_"+"EDL"*5+"HV_A"+"IR"*7+"XC"*6+"E____N__YJ_YT___L")[reduce(lambda a,x:a+ord(x)^24,s,0)%174]

Try it online!

Uses a simple hash function to find the second character of the abbreviation.

\$\endgroup\$
11
\$\begingroup\$

Python 2, 94 90 bytes

lambda s:s[0]+s[(19**9*0x4710b8f6019c1b61deca10eef13b1>>hash(s)%8199472%52*3&7)+1].upper()

Try it online

(Only Python 2 because Python 3 hashes are not stable and also you can't hash a string without turning it into bytes.)

Works with all 50 states, plus as a bonus District of Columbia and Puerto Rico.

It would be possible to save one byte by writing the number in base-36: int("5clu7x0aixb0pelmpugv5iiibphrpf",36). I'm waiting to see if I can think of a more creative solution.

Update:

Since there are 12 unused digit positions in the hash, there are 236 possible numbers which will work. It seemed reasonable to believe that one of them would have a prime factor with a large exponent. For any given prime and exponent, finding a value is quite fast; I managed to find one with a factor of 199, reducing the size of the expression needed to represent the number by four bytes.

This is basically the same procedure as my C answer but using Python. Since the base hash function is different, I had to find a different reduction function, which turns out to be mod 52 instead of mod 54. But the biggest savings over C come from the possibility of using bignums to encode the vector, and of course the fact that the standard for Python seems to be that it's ok to use a lambda literal instead of a complete program.

\$\endgroup\$
9
\$\begingroup\$

Retina, 113 81 80 77 70 68 bytes

M1!`.+[A-Zz]|...s.s?|M[io]n?t?|[AFIOUWN][^o]v?|T..|.*
\B.*(?=.)

T`l`L

Try it online! Includes all 51 test cases. Saved 32 bytes by appropriating @JörgHülsermann's regex that finds the second letter (with a tweak to support DC; edit: saved 1 byte thanks to @JörgHülsermann). Saved 3 bytes by switching to @SteveBennett's regex from his comment to @HPWilliam's answer. Saved 7 9 bytes thanks again to @SteveBennett. The other letters are then deleted and the string uppercased.

\$\endgroup\$
  • \$\begingroup\$ Very nice, wouldn't have thought you could get a set of fully removable strings. \$\endgroup\$ – colsw May 26 '17 at 9:18
  • \$\begingroup\$ @ConnorLSW e was the most troublesome, really. \$\endgroup\$ – Neil May 26 '17 at 9:19
  • \$\begingroup\$ @JörgHülsermann Thanks for the return favour! \$\endgroup\$ – Neil May 27 '17 at 20:50
  • \$\begingroup\$ @JörgHülsermann Oh, so I'll be able to save another byte then? \$\endgroup\$ – Neil May 27 '17 at 21:27
  • 1
    \$\begingroup\$ Wait, Indiana does become ID. You can probably fix it just by adding $ after the d. (You might want to use automatic testing - I have a script that constantly checks all 50 cases.) \$\endgroup\$ – Steve Bennett May 28 '17 at 10:07
8
\$\begingroup\$

PHP>=7.1, 113 Bytes

<?=($a=$argn)[0],ucfirst(preg_match('#[vxz]| .|owa|lask|[CGHKLPV].*|ssi?.|n(n|t|[de]$)#',$a,$t)?$t[0][-1]:$a[1]);

Online Version

The strikes are match through a earlier beginning match before

([vxz]) matches Arizona, Nevada, New Mexico, Pennsylvania, Texas,

(.) (one space before) matches New Hampshire, New Jersey, New Mexico, New York, North Carolina, North Dakota, Rhode Island, South Carolina, South Dakota, West Virginia

[CGHKLPV].*(.) matches California, Colorado, Connecticut, Georgia, Hawaii, Kansas, Kentucky, Louisiana, New Hampshire, North Carolina, Pennsylvania, South Carolina, Vermont, Virginia, West Virginia

ow(a) match Iowa

las(k) match Alaska

ssi?(.) matches Massachusetts, Mississippi, Missouri, Tennessee

n(n|t|[de]$) matches Connecticut, Kentucky, Maine, Maryland, Minnesota, Montana, Pennsylvania, Rhode Island, Tennessee, Vermont

No match for these states so we take the first two letters Alabama, Arkansas, Delaware, Florida, Idaho, Illinois, Indiana, Michigan, Nebraska, Ohio, Oklahoma, Oregon, Utah, Washington, Wisconsin, Wyoming

First time that I am use this Regex Subpatter ?| with allows to store the backreferences in one.

Support the District of Columbia

Replace (.) with ([^o]) +3 Bytes

Try it online!

PHP, 150 Bytes

<?=($t=preg_replace("#.\K\w+ |las|ri|nec|eorgi|awa|ow|[aio]ni?|e(?=n|v|x)|ntuck|ouisi|a?in|arylan|issi?|nnsylv|erm|irg#","",$argn))[0],ucfirst($t[1]);

Try it online! Testcases

\$\endgroup\$
  • 3
    \$\begingroup\$ Isn't n|t| a byte shorter than [nt]|? \$\endgroup\$ – Neil May 27 '17 at 18:18
  • \$\begingroup\$ @Neil Yes it is. I have not realize it. Thank You \$\endgroup\$ – Jörg Hülsermann May 27 '17 at 18:55
7
\$\begingroup\$

PHP, 887 854 bytes

<?=array_combine(['Alabama','Alaska','Arizona','Arkansas','California','Colorado','Connecticut','Delaware','Florida','Georgia','Hawaii','Idaho','Illinois','Indiana','Iowa','Kansas','Kentucky','Louisiana','Maine','Maryland','Massachusetts','Michigan','Minnesota','Mississippi','Missouri','Montana','Nebraska','Nevada','New Hampshire','New Jersey','New Mexico','New York','North Carolina','North Dakota','Ohio','Oklahoma','Oregon','Pennsylvania','Rhode Island','South Carolina','South Dakota','Tennessee','Texas','Utah','Vermont','Virginia','Washington','West Virginia','Wisconsin','Wyoming'],['AL','AK','AZ','AR','CA','CO','CT','DE','FL','GA','HI','ID','IL','IN','IA','KS','KY','LA','ME','MD','MA','MI','MN','MS','MO','MT','NE','NV','NH','NJ','NM','NY','NC','ND','OH','OK','OR','PA','RI','SC','SD','TN','TX','UT','VT','VA','WA','WV','WI','WY'])[$argv[1]];

Try it online!

First timer, hooray!

\$\endgroup\$
  • 1
    \$\begingroup\$ This way a little golfed In the Array are the values with space or where the second letter is correct removed. And $argv[1] is replaced with $argn sandbox.onlinephpfunctions.com/code/… \$\endgroup\$ – Jörg Hülsermann May 26 '17 at 20:20
  • \$\begingroup\$ @JörgHülsermann thanks a lot! I really enjoy your answers here on codegolf in php! \$\endgroup\$ – Ivanka Todorova May 26 '17 at 20:22
  • \$\begingroup\$ I am only a little light against other people here. The learning effort is good if someone finds an improvement. I hope that you answer more questions in the future \$\endgroup\$ – Jörg Hülsermann May 26 '17 at 20:31
7
\$\begingroup\$

C, 945 937 718 711 660 616 bytes

Saved 219 bytes thanks to ASCII-only.

struct{int*a,b;}m[]={"laba",76,"lask",75,"rizo",90,"rkan",82,"alif",65,"olor",79,"onne",84,"elaw",69,"lori",76,"eorg",65,"awai",73,"daho",68,"llin",76,"ndia",78,"owa",65,"ansa",83,"entu",89,"ouis",65,"aine",69,"aryl",68,"assa",65,"ichi",73,"inne",78,"issi",83,"isso",79,"onta",84,"ebra",69,"evad",86,"ew H",72,"ew J",74,"ew M",77,"ew Y",89,"orth",67,"orth",68,"hio",72,"klah",75,"rego",82,"enns",65,"hode",73,"outh",67,"outh",68,"enne",78,"exas",88,"tah",84,"ermo",84,"irgi",65,"ashi",65,"est ",86,"isco",73,"yomi",89};
i;char b[99];main(){gets(b);putchar(*b);for(;m[i].a;i++)if(!strncmp(m[i].a,b+1,4))puts(&m[i].b);}

Newline unnecessary, only for display purposes. Takes the state as input. Try it online!

How it works:

  • struct{int*a,b;}m[]=... declares a map m with two values - a four-byte string and one character. This is used in the comparison loop, which compares the second through fifth indexes with char*a in the map.
  • gets(b) reads a string b. This will be the state to abbreviate.
  • putchar(*b) prints the first character of that string, since each abbreviation starts with the first letter of the state.
  • for(;m[i].a;i++) loops through each value of the map. (This might be able to be shortened.)
  • if(!strncmp(m[i].a,b+1,4)) compares the current map value to the second through fifth characters of b (the state to abbreviate). This is because the only differences are in the first five characters, but we've already printed the first character.
  • puts(&m[i].b); prints the second letter of the abbreviation (if the state name matches with the current map value), and a newline.
\$\endgroup\$
  • \$\begingroup\$ This seems to produce wrong output for the North*/South* states. \$\endgroup\$ – Felix Dombek May 26 '17 at 15:43
6
\$\begingroup\$

C, 148 141 bytes

main(){char s[99];gets(s);printf("%c%c\n",*s,s["-2-1--561-1-62--642138364---4142--1416--67-7131-111-7-246"[*(int*)(s+1)%663694%57]-48]&95);}

*(int*)(s+1) considers the second through fifth character of the input to be an integer; that integer is then hashed into 0-56 using the hash i%663694%57. The hashed value is then looked up in a vector of offsets which represent the location of the second letter of the abbreviation. I chose those particular four bytes because (1) Missouri and Mississippi first differ in the fifth character and (2) some states have only four characters In C you can use the the NUL terminator byte, but nothing beyond that is reliable. (This hashes both Norths to the same value, as well as both Souths. But that doesn't matter because the associated offset is 6 for all of these.)

As it happens, that particular hash yields the correct position for the second letters of the abbreviations of District of Columbia, Puerto Rico and "Virgin Islands" (typed that way, not as "US Virgin Islands", because the algorithm insists that the first character of the abbreviation be the first character of the name).

The constants 663694 and 57 were found with an automated test; 57 was the smallest hash range I found. (The first version used 380085 and 63, but when I extended the test range I found the new one.) It seems that a slightly smaller hash exists if a code is added for "use the last character in the name"; unfortunately, C syntax for selecting the last character is too wordy to make that helpful.

There are only 8 different offsets, so they could have been stored in a 171-bit (3*57) lookup table with three bits per entry. But I couldn't think of a way to insert those bits efficiently into the program. Hex-encoding would require about one character per four bits, plus the 0x prefixes. I couldn't do better than 151 bytes, which is much longer than the string version. If the 171 bits could somehow be inserted as raw octets, they would occupy 22 bytes, so there might be a solution, but reading a file is clunky.

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

Actually, 181 bytes

2"OHCALAGAMAWVFLNVILMNMOMIRINCDEMTMEINWANYTXORNEOKIDAZNMUTNDMDVAKYSDPAARWYNHIAMSALNJAKTNHIKSVTWICOSCCT"╪"âäà♠îÉæô↨→←∟♣áíå*,▓/12│┤94▼╛?DE╞G╚╠╬ST╒WXßb;Θoq╙|⌂"♂┘Z⌠i≈┐⌡MXO;rR5♀ⁿ*:236@%└

This solution expects input as a quoted string.

Try it online!

Explanation

This solution utilizes the same hashing strategy as my Python 3 solution. For brevity, I am going to omit the explanation of how the hash is computed and why it was chosen (go read the other answer if you want that bit).

Also for brevity, I'm going to be leaving out the contents of the very long strings, since otherwise the explanation would be unreadable.

2"..."╪"..."♂┘Z⌠i≈┐⌡MXO;rR5♀ⁿ*:236@%└
2"..."╪                                state abbreviations (push the long string, split into length-2 chunks)
       "..."♂┘                         hash values for the state names (a string of CP437-encoded characters, converted to their CP437 ordinals)
              Z                        zip the two lists
               ⌠i≈┐⌡M                  for each pair:
                i                        flatten the pair
                 ≈                       convert hash value to int
                  ┐                      store abbreviation at the register numbered by the hash value
                     X                 discard the now-empty list
                      O                convert input string to list of ASCII ordinals
                       ;rR             range(len(ordinal_list)), reversed
                          5♀ⁿ          5**i mapped over that range
                             *         dot product of powers of 5 and ordinal list
                              :236@%   mod by 236
                                    └  push value in that register
\$\endgroup\$
3
\$\begingroup\$

Python 3, 230 bytes

lambda s:chr(s[0])+'IA%%L!NOI!M%!E.N!Y.XR.Z$D.I!.D$DA.D%!.HA!LJ%.N%‌​$T.I%!C!T!.HAAT$.A!.‌​VL.V%$CE%%AEK%.T$!.Y‌​.A!.R.Y$O.S%!.K$!.S'‌​.replace('%','$$').r‌​eplace('$','!!').rep‌​lace('!','..')[sum(c‌​*5**i for i,c in enumerate(s[::-1]))%236-5]

Try it online!

Input is expected as a bytes object (a byte string, rather than a Unicode string).

Thanks to Johnathon Allan for an absurd amount of bytes

Explanation

Each state name is hashed to an integer a by applying the hash a = sum(o*5**i) % 236 (where o is a character's ASCII ordinal and i is its index in the string, counting back from the end). The modulus 236 was chosen because it is the smallest modulus that causes all hash values to be distinct for the 50 US state names. These hashes are then mapped to the state abbreviations, and the resulting dictionary (compressed using string substitution) is used to look up the abbreviation given a state name (hashing it to get the appropiate key).

\$\endgroup\$
  • \$\begingroup\$ Save 179 bytes with lambda s:chr(s[0])+'.....IA................L..NOI..M..........E.N..Y.XR.Z....D.I...D....DA.D...........HA..LJ.........N............T.I..........C..T...HAAT.....A...VL.V............CE................AEK.........T.......Y.A...R.Y....O.S...........K.......S'[sum(c*5**i for i,c in enumerate(s[::-1]))%236] \$\endgroup\$ – Jonathan Allan May 26 '17 at 10:58
  • \$\begingroup\$ ...and another 51 on top of that with lambda s:chr(s[0])+'IA%%L!NOI!M%!E.N!Y.XR.Z$D.I!.D$DA.D%!.HA!LJ%.N%$T.I%!C!T!.HAAT$.A!.VL.V%$CE%%AEK%.T$!.Y.A!.R.Y$O.S%!.K$!.S'.replace('%','$$').replace('$','!!').replace('!','..')[sum(c*5**i for i,c in enumerate(s[::-1]))%236-5] \$\endgroup\$ – Jonathan Allan May 26 '17 at 11:46
  • \$\begingroup\$ I'm surprised that counting backwards costs fewer bytes than a hash function that counts forwards -- but I can't find one with a little playing \$\endgroup\$ – Chris H May 26 '17 at 15:41
  • 1
    \$\begingroup\$ @ChrisH I thought I found one, but the compressed string is more expensive. \$\endgroup\$ – Mego May 26 '17 at 16:14
2
\$\begingroup\$

Ruby, 106 103 bytes

->s{s[0]+(s=~/ /?$'[0]:s[(j="()6>P_ac;?.O}AFLKMrS".index((s.sum%136%95+32).chr))?j>7?j/4:-1:1]).upcase}

If the input contains a space, the second output letter is the one after the space. Else...

Hash the sum of all characters in the input to obtain a character whose index in the magic string indicates the index of the second output letter in the input string, according to the formula j>8?j/4:-1 (-1 means the end.). If the hash gives a character that is not in the magic string, second letter is second letter of input.

As an explanation of the magic string, the hash characters and the letter indexes they encode are below. Note that Delaware appears even though the second letter would do - this is because its hash code clashes with Kentucky. Fortunately the last letter of Delaware is the same as the second.

Letter(index)
Last  (-1)  (-MD    )-VA    6-GA-LA  >-DE-KY    P-PA    _-CT    a-KS    c-VT
3rd    (2)  ;-TN    ?-MN    .-TX     O-NV
4th    (3)  }-MS    A-IA    F-MT     L-AZ
5th    (4)  K-MO    M-AK    r-ME     S-HI 

Ungolfed in test program

a="Alabama
Alaska
Arizona
Arkansas
California
Colorado
Connecticut
Delaware
Florida
Georgia
Hawaii
Idaho
Illinois
Indiana
Iowa
Kansas
Kentucky
Louisiana
Maine
Maryland
Massachusetts
Michigan
Minnesota
Mississippi
Missouri
Montana
Nebraska
Nevada
New Hampshire
New Jersey
New Mexico
New York
North Carolina
North Dakota
Ohio
Oklahoma
Oregon
Pennsylvania
Rhode Island
South Carolina
South Dakota
Tennessee
Texas
Utah
Vermont
Virginia
Washington
West Virginia
Wisconsin
Wyoming".split($/)

f=->s{                                                            #String argument s.
  s[0]+(                                                          #Return character s[0] +
    s=~/ /?$'[0]:                                                 #if s contains a space, 1st character after space, ELSE
      s[(j="()6>P_ac;?.O}AFLKMrS".index((s.sum%136%95+32).chr))?  #if (sum of ascii codes, mod 136 mod 95 +32).chr in the magic string
        j>7?j/4:-1:                                                 #return s[j/4] if j>7 else return s[-1] ELSE
      1]                                                          #if not in the magic string, return s[1].
  ).upcase                                                        #Convert the second character to uppercase if needed.
}


a.map{|i|p [i,f[i]]}
\$\endgroup\$
2
\$\begingroup\$

///, 619 608 bytes

/2/~M//@/~South //1/~North //!/~New //~/\/\///Alabama/AL~Alaska/AK~Arizona/AZ~Arkansas/AR~California/CA~Connecticut/CT~Delaware/DE~Florida/FL~Georgia/GA~Hawaii/HI~Idaho/ID~Illinois/IL~Indiana/IN~Iowa/IA~Kansas/KS~Kentucky/KY~Louisiana/LA2aine/ME2aryland/MD2assachusetts/MA2ichigan/MI2innesota/MN2ississippi/MS2issouri/MO2ontana/MT~Nebraska/NE~Nevada/NV!Hampshire/NH!Jersey/NJ!Mexico/NM!York/NY1Carolina/NC1Dakota/ND~Ohio/OH~Oklahoma/OK~Oregon/OR~Pennsylvania/PA~Rhode Island/RI@Carolina/SC@Dakota/SD~Tennessee/TN~Texas/TX~Utah/UT~Vermont/VT~Virginia/VA~Washington/WA~West Virginia/WV~Wisconsin/WI~Wyoming/WY/

Try it online!

Since there is no other way of taking input in ///, it goes at the end of the program. Just append the desired input to the program.

Saved 11 bytes by making more replacements, as recommended by @SteveBennett

\$\endgroup\$
  • \$\begingroup\$ There's probably some patterns you can replace multiple times at once, like "New " and "akota". Annoying that you can't do much smarter like removing sections of state names, because converting the remaining character to uppercase is so expensive... \$\endgroup\$ – Steve Bennett May 27 '17 at 5:48
  • \$\begingroup\$ @SteveBennett Edited, thanks! \$\endgroup\$ – Comrade SparklePony May 27 '17 at 12:13
2
\$\begingroup\$

Python 2, 131 125 bytes

lambda s:s[0]+'CLLKARADEZVAK.T..DETTH.NSAHY...ID.D..O..Y.IRE.X..NALINC.VJM.SY.T..AAOI'[int(s[0]+s[-2:],36)%386%334%181%98%70]

Try it online!

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

TAESGL, 386 bytes

B=«ōďā,AL,ņćđ,AK,ķċđ,AZ,ćōē,AR,ďċđ,CA,ĭāď,CO,ŕĭ,CT,ćđēą,DE,ĕŕ,FL,īĭ,GA,ńāē,HI,ćĉďą,ID,ĭċď,IL,ľđā,ţ,ńĕĕ,IA,ķő,KS,ŏĝ,KY,ŏĕĕ,LA,ŏđć,ME,ņāē,MD,ńđā,MA,īđą,MI,ļēď,MN,ŕğ,MS,ňė,MO,ććĕĉ,MT,ćċćĉ,NE,ŕēď,NV,ň ćŋā,NH,ň ĩēđ,NJ,ň ğĕċ,NM,ň ĉĝ,NY,ćņ ġĉă,NC,ćņ ńċą,ND,ĩēą,OH,ŋĺ,OK,ļķ,OR,ĺđď,PA,ĉĉğ đēā,RI,ōċ ġĉă,SC,ōċ ńċą,SD,ňďą,TN,ċĕď,TX,ōđą,UT,ćđāā,VT,ğğ,VA,ďĉē,WA,ĉĉć ğğ,WV,ľēđ,WI,ĉĩĕ,WY»Ĵ",";B[BĪA)+1

Interpreter

Very simple compression of the state names, added into an array with the abbreviations.

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

Japt, 383 bytes

The compression of the first string may be improvable by experimenting with the order of the letters.

g +`lkzÇUaidlnyaÀÍ¥evhjmycdhkÎödnxttaaviy`g`alabaµ
Ã2ka
iza
kÂ6s
Öâfnia
åªv
¬nש
Ü.Ø
fÓQ»
gegia
°ii
i»
ÅJno
Äa
Å0
kÂ6s
kÀ_cky
lia
Úpe
æ¯À
ÚUaÖ³etts
Úòig
·nÌta
æ«7ppi
æ¬
Úa
ßka
va»
w mp¢i
w jÀ y
w ´xi¬
w yk
Íh ÖÚ¦na
Íh »kota
oo
oklaÊá
eg
pnsylvia
r¸ Ó
Ñh ÖÚ¦na
Ñh »kota
âÊte
x
©ah
vÚ
virgia
Øgn
ØÙ virgia
æÈ;n
wyÇg`·bUv) u

Try it online

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

Mathematica, 138 140 134 Bytes

+2 bytes - found a mistake (needed array offset of 1 not 0)

-6 bytes - found a better hash

#~StringTake~1<>"R_ATE__IN_COI_J_I_SLNAT_Y_Y_HKOAE__SAA_DDLM_RVAH_XDTVA__I_N_EA_T_DY_C_KZL"~StringTake~{1+Hash@#~Mod~89866736~Mod~73}&

Similar to others it takes the name and takes the first letter. Then it applies the default Mathematica hash then applies two modulus to it "Hash@#~Mod~89866736~Mod~73" to get a unique number for each state. This value is then looked up in a string to generate the second letter.

Can probably be golfed more but the search space is huge for Mathematica to find. Duplicated second letters weren't considered in the hash search. _ characters represent wasted values in the string. In theory you could get the string down to only 19 characters but finding the custom hash to produce that would be a nightmare.

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

Perl 5, 150 148 bytes (147 + 1)

This is by no means optimal, but it does its job. Needs -n command line flag.

s/las//;s/ai?n//;s/[oie]n|ri//;s/e([vx])/$1/;s/issi?//;s/(.).+ /\1/;/(.)(.)/;/^([^W]).*(?:[cogavn][wiku]|[ir][ys][li]|rm)([adyti])$/;print uc"$1$2"
\$\endgroup\$
0
\$\begingroup\$

Python 2, 152 bytes

lambda s:s[0]+'.NY.SDS...O.DT..RT.AAYJZE.K.I.X.TI.EL.CMI..E.NA..L....TH.......O....DAAC..VNH.YAI.RVDA..L....A'[int(s.replace(' ','')[1:],36)%358%235%95]

Try it online!

A port of Arnauld's answer.

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