9
\$\begingroup\$

Given a the name of a state of the United States as a string (with case), return the number of votes the state has in the Electoral College. Write a full program or function, and take input and output through any default I/O method.

A list of all inputs and outputs (source):

[['Alabama', 9], ['Alaska', 3], ['Arizona', 11], ['Arkansas', 6], ['California', 55], ['Colorado', 9], ['Connecticut', 7], ['Delaware', 3], ['Florida', 29], ['Georgia', 16], ['Hawaii', 4], ['Idaho', 4], ['Illinois', 20], ['Indiana', 11], ['Iowa', 6], ['Kansas', 6], ['Kentucky', 8], ['Louisiana', 8], ['Maine', 4], ['Maryland', 10], ['Massachusetts', 11], ['Michigan', 16], ['Minnesota', 10], ['Mississippi', 6], ['Missouri', 10], ['Montana', 3], ['Nebraska', 5], ['Nevada', 6], ['New Hampshire', 4], ['New Jersey', 14], ['New Mexico', 5], ['New York', 29], ['North Carolina', 15], ['North Dakota', 3], ['Ohio', 18], ['Oklahoma', 7], ['Oregon', 7], ['Pennsylvania', 20], ['Rhode Island', 4], ['South Carolina', 9], ['South Dakota', 3], ['Tennessee', 11], ['Texas', 38], ['Utah', 6], ['Vermont', 3], ['Virginia', 13], ['Washington', 12], ['West Virginia', 5], ['Wisconsin', 10], ['Wyoming', 3]]

Any other input is undefined behavior. This is , so the shortest solution in bytes wins.

Hint: You probably shouldn't store all of the state names.

If you're solving this challenge with a builtin, please also write a solution without, for the sake of an interesting answer.

\$\endgroup\$
  • 7
    \$\begingroup\$ There's probably a builtin in Mathematica for this \$\endgroup\$ – user45941 Mar 8 '16 at 18:46
  • 3
    \$\begingroup\$ If this goes on further we need a tag for the US presidential election ^^ \$\endgroup\$ – Denker Mar 8 '16 at 18:55
  • \$\begingroup\$ Haha lol no, there probably isn't a builtin for something as crazy as that. \$\endgroup\$ – CalculatorFeline Mar 8 '16 at 22:08
  • \$\begingroup\$ The inputs and outputs need to be provided in this question, so that old answers do not become invalid after the next census. \$\endgroup\$ – Sparr Dec 1 '18 at 8:12
  • 1
    \$\begingroup\$ Just mentioning for anyone else tripped up by this that DC is not in the above list, so it doesn't sum to 538. \$\endgroup\$ – Steve Bennett May 8 at 12:17
9
+100
\$\begingroup\$

Python 3, 137 bytes

lambda s:b"2F$&#+*&4#(#'*&$++&''0+)-/ &)$# #(0*W% *&4)$= ,   %.# = %#$"[sum(b'!!E$/!5.!!!&#"!!1_&!!$#<!./'[ord(c)%32]-32for c in s)-4]-32

Although not required by the challenge, this accepts case-insensitive state names. I computed parts of this hash function with GPerf.

| improve this answer | |
\$\endgroup\$
5
\$\begingroup\$

Python 2, 289 bytes

c="las,De,Mo,Da,Ve,Wy 3 Ha,Id,ai,Rh 4 eb,xi,st 5 ka,Io,Ka,pp,ev,U 6 ct,Ok,go 7 Ke,ui 8 Al,ad,Sou 9 ry,ta,ou,Wi 10 iz,nd,tt,ee 11 sh 12 Vi 13 Je 14 ro 15 Ge,ch 16 Oh 18 ll,yl 20 Fl,Yo 29 xa 38 if 55"
f=0
u=input()
for i in c.split():
 if f:print i;break
 f=any(j in u for j in i.split(','))

Example

$ python2 test.py
"Tennessee"
11

$ python2 test.py
"California"
55

$ python2 test.py
"Rhode Island"
4
| improve this answer | |
\$\endgroup\$
3
\$\begingroup\$

JavaScript (Node.js),  147 145  127 bytes

s=>Buffer("@>~;;F~<~;~?~~^~UL~~C~~?~<<@~EBB~B?~>~~~~AD>~A~=;~>=>H~~~C>A~BG<H;C<CoUJ~~=;L")[parseInt(s[8]+0+s,35)%561%263%80]-56

Try it online!

How?

Transforming the input string

In this paragraph, we temporarily assume that we're going to convert some transformation of the input string from base 36 to decimal.

We can't just pass the state name to parseInt() to turn it into a unique numeric identifier because some state names include a space that would cause the parsing to stop, whatever base is used. For instance, "North Carolina" and "North Dakota" would be both parsed as "north".

Possible solutions:

  • We could remove the space or replace it with another character. But this is a lengthy operation.

  • We could pick 3 arbitrary letters. Any permutation of either (s[2],s[4],s[6]) or (s[0],s[4],s[8]) would work. This is better but still a bit lengthy.

  • Another possible strategy is to prepend the 9th character to the full name. For instance, "North Carolina" is turned into "rNorth Carolina" and parsed as "rnorth", while "North Dakota" is parsed as "knorth".

The last strategy looks promising, but we have a problem with "Michigan" vs "Missouri". When the length of a state is less than 9, "undefined" is inserted instead, leading to a rather long string which is likely to cause a loss of precision. That's why "Michigan" and "Missouri" are both parsed as "undefinedmh000000", where mh000000 is the rounded value of mi.......

Fortunately, we can fix that by doing:

s[8] + 0 + s

This time, when the 9th character is missing, it is turned into undefined + 0 = NaN. So, "Michigan" is parsed as "nanmichiga8" and "Missouri" is parsed as "nanmissourk" (only the last 'digit' is rounded).

Choosing the base

With this transformation, we can use any base between 32 and 36. In base 32, the characters w, x, y and z cause the parsing to stop. But the states that contain these letters are still turned into unique values:

 State           | Turned into       | Parsed as
-----------------+-------------------+--------------
 "Arizona"       | "NaNArizona"      | "nanari"
 "Delaware"      | "NaNDelaware"     | "nandela"
 "Hawaii"        | "NaNHawaii"       | "nanha"
 "Iowa"          | "NaNIowa"         | "nanio"
 "Kentucky"      | "NaNKentucky"     | "nankentuck"
 "Maryland"      | "NaNMaryland"     | "nanmar"
 "New Hampshire" | "s0New Hampshire" | "s0ne"
 "New Jersey"    | "e0New Jersey"    | "e0ne"
 "New Mexico"    | "c0New Mexico"    | "c0ne"
 "New York"      | "NaNNew York"     | "nanne"
 "Pennsylvania"  | "a0Pennsylvania"  | "a0penns"
 "Texas"         | "NaNTexas"        | "nante"
 "Wyoming"       | "NaNWyoming"      | "nan"
 "West Virginia" | "g0West Virginia" | "g0"
 "Wisconsin"     | "n0Wisconsin"     | "n0"
 "Wyoming"       | "NaNWyoming"      | "nan"
 "Washington"    | "o0Washington"    | "o0"

See the full tables in base 32 to base 36

Choosing the hash function

We can now try to brute-force a hash function that leads to the shortest possible lookup table.

I've actually tried many different things, but the pattern that worked best was:

parseInt(s[8] + 0 + s, B) % M0 % M1 % M2

with 32 ≤ B ≤ 36, 50 ≤ M0 < 1000, 50 ≤ M1 < M0 and 50 ≤ M2 < M1.

This eventually led to:

parseInt(s[8] + 0 + s, 35) % 561 % 263 % 80

and a lookup string of 77 characters.

| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ Great answer (and appropriate timing)! \$\endgroup\$ – user Nov 4 at 19:06
  • 1
    \$\begingroup\$ @user Well, it's actually 6-month old and I forgot about it. Today, I looked for a challenge about the Electoral College, found this one ... and this answer of mine which I've just slightly improved. \$\endgroup\$ – Arnauld Nov 4 at 19:44
  • \$\begingroup\$ Oh, I see, I thought you'd answered today instead of just edited it. \$\endgroup\$ – user Nov 4 at 20:58
2
\$\begingroup\$

Retina, 210 bytes

Wi|Min|Mar|ri$
10
G|J|Mas|^Vi|rth C|Oh|Wa|ch
1
Ari|In|Ten
11
F|Y|gt
2
lv|is$
20
las|D|Mo|T|(1)r|g$
$1 3
H|R|Ma|Id|rs
4
al|if|eb|(1)a|V|Mex
$1 5
Ar|gi?a|Io|Ka|pi|Nev|U
6
cu|O
7
[KLx]|io$
8
Al|do|id|rk| C
9
T`Ll 

The last line has a trailing space. Output contains a trailing newline. If that's a problem, it'll be one more byte

Version for all states has m` in few places to make it work with multiple lines.

Try it online!
Try it online with all states!

| improve this answer | |
\$\endgroup\$
2
\$\begingroup\$

JavaScript (ES6) 333 331 329 223 characters

Many thanks to ETHproductions and Neil, 100+ characters thanks to both of you, and I haven't tried findIndex() yet :-)

s=>(d="0,;y\\>|Kb^6 |kn|xw|r?||8MD|hQ|EU|7|2||R|e||||F|||||||||G".split("|"),d[35]="",d[52]="3",d.map((a,j)=>{if(~a.indexOf(String.fromCharCode([...s].reduce((S,c)=>S*32+c.charCodeAt(),0)%153)))o=j}),o+3)

Expanded version

(With \0xx instead of characters)

s => (
    d = "0,;y\\>|Kb^6\t|kn\x1D|\x05\x7F\x84x\bw|r\x12?|\x07\x98|8MD|\x93h\x8E\x11Q|\x8FEU\x1B|7|2|\v|R|\x92e||\x19||\x04F|||||||||\x94G".split("|"),
    d[35] = "\x13",
    d[52] = "3",
    d.map(
        (a,j) => {
            if(~a.indexOf(
                String.fromCharCode([...s].reduce(
                    (S,c)=>S*32+c.charCodeAt(),0)%153)
                ))
                o = j
        }
    ),
    o+3
)

Approach:

Preprocessing:

Calculate an hash of each state name and store them in an array where one dimension is the number of votes the state has.

Processing:

Recalculate the hash and retrieve the information.

An hash of a state name is calculated with s.split('').reduce((S,c)=>S*32+c.charCodeAt(0),0)%153.

It transform "Iowa" in (32^3*'I' + 32^2*'o' + 32*'w' + 'a')%153 (with ascii value for characters).

Why 32 and 153? Because after a few empiric tests those values minimize hashes without collision between states that have different number of votes.

I don't believe that it will be hard to do something shorter with a better approach but since I spent a few time on it ;).

| improve this answer | |
\$\endgroup\$
  • 1
    \$\begingroup\$ Nice! s.split('') can be changed to [...s]. You may also be able to save some bytes by formatting the list of lists as a string where each number is represented by its char code. \$\endgroup\$ – ETHproductions Mar 11 '16 at 20:29
  • 1
    \$\begingroup\$ Drop the 0 from charCodeAt(). Also the a&& is unnecessary because map won't enumerate holes. You should also write your function as an expression to avoid the explicit return (use (,)s instead of {;}s). \$\endgroup\$ – Neil Mar 12 '16 at 0:07
  • \$\begingroup\$ findIndex might work out superior to map, especially if you can arrange to use its default value (i.e. drop the first element of the array and add an extra 1 at the end to compensate). I think I have it at 284 bytes. \$\endgroup\$ – Neil Mar 12 '16 at 0:10
1
\$\begingroup\$

Ruby, 232 bytes

Uses an array of regular expressions that each represent one bit of the elector count.

r=[/[bzCDFYV]|om|e[tge]|Me|di|sk|Mo/,/[zkfDJOTU]..|o[nwtm].|[lai].s|[edy].a|rt/,/[fFHvJxYRUg]...|[hnIg]o|[srx].s|ct|M.i|r.*C/,/[zlLsYrtc]o.|se|ba|y$|ry|di|^Vir/,/[fFGYP]|Oh|ig|Il/,/xa|al/];h=->s{t=0;6.times{|i|s=~r[i]?t+=2**i :0};t}
| improve this answer | |
\$\endgroup\$
1
\$\begingroup\$

Mathematica / Wolfram Language 235 bytes.

I use a built-in for state names.

f[x_] := Select[
   Transpose[{CountryData["UnitedStates", 
       "AdministrativeDivisions"][[All, 2, 1]], {9, 3, 11, 6, 55, 9, 
      7, 3, 0, 29, 16, 4, 4, 20, 11, 6, 6, 8, 8, 4, 10, 11, 16, 10, 6,
       10, 3, 5, 6, 4, 14, 5, 29, 15, 3, 18, 7, 7, 20, 4, 9, 3, 11, 
      38, 6, 3, 13, 12, 5, 10, 3}}], #[[1]] == x &][[1, 2]]
| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ This answer is valid now. \$\endgroup\$ – Dennis Dec 1 '18 at 14:05
1
\$\begingroup\$

C (gcc), 289 bytes

Truncates incoming string until it is found in lookup. Immediately preceding every partial state name is the number of electoral votes. Dual-word names are shortened to save some space on the pattern "Foo Bar" -> "FBar".

The average length of each entry is 2.3, and I had real trouble finding a good hash shorter than that.

Still, too many strX() calls lying around.

f(s){char*t=strdup(s),*p=strchr(t,32);for(p++&&memmove(t+1,p,strlen(p)+1);*t*!(p=strstr(")Alab#Alas+Ari&ArkWCa)Col'Con#D=F0G$H$Id4Il+In&Io&Ka(Ke(L$Mai*Mar+Mas0Mic*Min&Missi*Misso#Mo%Neb&Nev$NH.NJ%NM=NY/NC#ND2Oh'Ok'Or4P$R)SC#SD+TenFTex&U#Ve-Vi,Wa%WV*Wi#Wy",t));t[strlen(t)-1]=0);s=*--p-32;}

Try it online!

| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ Suggest index(t,32);for(p++?bcopy(p,t+1,strlen(p)+1):0 instead of strchr(t,32);for(p++&&memmove(t+1,p,strlen(p)+1) \$\endgroup\$ – ceilingcat Dec 25 '18 at 23:24
1
\$\begingroup\$

Ruby, 164 bytes

s=gets
g=9
41.times{|i|s[j=i/39,i/8+3].index('IndaMWictAbNekgKO
w ymuTxUSRPGLFvzfJYDHaip'[i,1+j])&&(g=":-1*1206))/5+,3,-8,+0).1L,/*:6.C,1]4C)**,"[i].ord-38)}
puts g

Try it online!

Explanation

I searched for a single character from the name to identify each state. This worked for about a third of the states, then I started truncating the names on the right hand side. This worked for all states except Maine (which is at the top of the list below and is identified by the string ai, along with Hawaii which has the same number of votes.) The program works in reverse, starting with the assumption that the state is Colorado, and revising to a different state each time a character is found. The state length is truncated to i/8+3 characters. A special consideration is given to Mississippi because its identifying character p is so far to the right that it has to be checked at the end.

This is a full program, and input is expected to be newline terminated (as it would at the Ruby console.) This is important because the newline is used as the identifying character for Ohio (when this test is done the string is truncated to 5 characters, and Ohio is the only 4-letter state which does not have another test, so this identifies it.)

Data used to build the program

i=40 chars=8+1=9
ip['Mississippi', 6],
32..39=7
ai ['Maine', 4],  ['Hawaii', 4],
H['New Hampshire', 4],
D['North Dakota', 3], ['South Dakota', 3], ['Delaware', 3], 
Y['New York', 29], 
J['New Jersey', 14],
f['California', 55],
z['Arizona', 11],
v['Nevada', 6],
i=24..31 chars=6
F['Florida', 29],
L['Louisiana', 8],
G['Georgia', 16],
P['Pennsylvania', 20],
R['Rhode Island', 4], 
S['South Carolina', 9],
U['Utah', 6],
x['Texas', 38]
i=16..23 chars=5
T['Tennessee', 11],
u['Kentucky', 8],
m ['Wyoming', 3]['Vermont', 3],
y['Maryland', 10],
SPACE['New Mexico', 5], ['West Virginia', 5],
w['Iowa', 6],
NEWLINE['Ohio', 18]
O['Oklahoma', 7], ['Oregon', 7], 
i=8..15 chars=4
K['Kansas', 6],
g['Virginia', 13],
k['Arka', 6], 
e['Nebr', 5],
N['Nort', 15], 
b['Alab', 9],
A['Alas', 3],
t['Mont', 3],
i=0..7 chars=3
c['Mich', 16],  
i['Minn', 10],  ['Miss', 10],  ['Wisc', 10],
W['Wash', 12],, 
M['Mass', 11],
a['Idah', 4],
d['Indi', 11], 
n['Conn', 7],
I['Illi', 20],
Initial guess
 ['Colo', 9], 
| improve this 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.