26 Singers, 26 Letters

According to RollingStone, below are the 26 greatest singers of all time:

Aretha Franklin         Al Green
Ray Charles             Robert Plant
Elvis Presley           Mick Jagger
Sam Cooke               Tina Turner
John Lennon             Freddie Mercury
Marvin Gaye             Bob Marley
Bob Dylan               Smokey Robinson
Otis Redding            Johnny Cash
Stevie Wonder           Etta James
James Brown             David Bowie
Paul McCartney          Van Morrison
Little Richard          Michael Jackson
Roy Orbison             Jackie Wilson


You can get this as a list of strings here.

Given a singer name, print or return a letter from A to Z which uniquely identifies this singer. (If your code returns A for Bob Dylan, then it cannot return A for any other singer.)

As opposed to other similar challenges, the mapping is up to you as long as it's collision-free.

Rules

• The input is guaranteed to be one of the 26 singer names listed above with this exact spelling and without any leading or trailing whitespace.
• You may output the letter in either lowercase or uppercase. But it must be consistent.
• You are encouraged to provide a test suite for all 26 possible inputs.
• This is , so the shortest answer in bytes wins!
• (Deleted sandbox entry.) – Arnauld Oct 10 '17 at 7:24
• Dear Rolling Stone: Bob Dylan is truly one of the greatest musicians ever. But a great singer? – Luis Mendo Oct 10 '17 at 10:45
• @LuisMendo I'm a bit salty about a few of these choices as well (cough cough where's Steve Tyler cough) – Lord Farquaad Oct 10 '17 at 16:07
• @LordFarquaad Steve Tyler is #99 ¯\_(ツ)_/¯ – Arnauld Oct 10 '17 at 16:46
• this may help somebody but I don't have the CG skills to use the info: the 1-6, 1-8, and 3-5 letters of the names are unique combinations. – Jeutnarg Oct 10 '17 at 17:25

MATL, 28 bytes

s98\40\2Y2'ijkl o qst uz'hw)


Try it online!

Explanation

s98\40\


Implicitly get the input string. Sum the characters of the input string, and do it modulus 98 followed by modulus 40. Results in one of the following numbers: 38 18 13 34 29 23 27 30 5 28 22 1 0 16 7 32 8 14 3 36 25 4 2 6 24 35 (in order of the Pastebin list).

2Y2'ijkl o qst uz'h


Push the (lowercase) alphabet with 2Y2. This takes care of the numbers in the range [1,26]. However, some numbers are missing, and we have numbers up to 38. Hence, we append (h) a string that takes care of the higher numbers, by mapping these numbers to the 'missing' letters. The spaces can be anything, I used capital letters in my original program for my own convenience.

w)


We can now index the number from the first step into the string from the second step with ). We use w to get the arguments in the correct order. While it may seem that we use 0-based indexing (the numbers vary from 0 to 38, and the string is 39 characters long), the reality is actually a bit more complicated: we use 1-based modular indexing, a feature unique to MATL. This means that 1 indexes to a, 38 actually indexes to u, and 0 indexes to the final z of the string.

Python 2, 80 71 bytes

def f(s):i=sum(map(ord,s))%98%40;return chr(i-i/26*18+i/32*3-i/37*8+65)


Try it online!

The sums of ordinals modded give numbers between 0 and 38

The numbers larger than 25 are then shifted to fill in the blanks as below (sorted sequence shown):

  0  1  2  3  4  5  6  7  8  -  -  -  - 13 14  - 16  - 18  -  -  - 22 23 24 25  - 27 28 29 30  - 32  - 34 35 36  - 38


Subtract 18 if i>25:

  0  1  2  3  4  5  6  7  8  -  -  -  - 13 14  - 16  - 18  -  -  - 22 23 24 25
- 27 28 29 30  - 32  - 34 35 36  - 38


Add 3 if i>31:

  0  1  2  3  4  5  6  7  8  -  -  -  - 13 14  - 16  - 18  -  -  - 22 23 24 25
- 27 28 29 30
- 32  - 34 35 36  - 38


Subtract 8 if i>37:

  0  1  2  3  4  5  6  7  8  -  -  -  - 13 14  - 16  - 18  -  -  - 22 23 24 25
- 27 28 29 30
- 32  - 34 35 36
- 38


Which gives the sequence 0..25

  0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25


These are then converted to A-Z with chr(i+65)

• I think you can shorten (i>31) to i/32, etc – xnor Oct 10 '17 at 8:58

6502 machine code routine (C64), 83 bytes

20 FD AE 20 9E AD 85 FC 20 A3 B6 A9 79 85 FB A0 00 84 FD B1 22 10 03 69 A0 18
45 FD 65 FB 85 FD E6 FB C8 C4 FC D0 EC E9 29 B0 FC 69 29 C9 1A 90 1C 29 0F C9
0D 90 04 69 09 90 12 C9 02 F0 0F C9 08 D0 04 A9 06 D0 06 C9 0C D0 02 A9 11 18
69 41 4C D2 FF


This is position-independent code, just put it somewhere in RAM and jump there, e.g. using the sys command.

Online demo (loads to $C000/49152). Usage: sys49152,"[name]", e.g. sys49152,"Aretha Franklin". Important: If the program was load from disk (like in the online demo), issue a new command first! This is necessary because loading a machine program trashes some C64 BASIC pointers. Note: The C64 is by default in a mode without lowercase letters -- in order to be able to enter readable names, switch to lowercase mode first by pressing SHIFT+CBM. Explanation The challenge is in fact to find a minimal perfect hash function for these names; for the C64, I had to find one that's easily computable in simple 8bit operations. Here's a commented disassembly listing: .C:c000 20 FD AE JSR$AEFD          ; consume comma
.C:c003  20 9E AD    JSR $AD9E ; evaluate expression .C:c006 85 FC STA$FC            ; save string length
.C:c008  20 A3 B6    JSR $B6A3 ; free string .C:c00b A9 79 LDA #$79           ; value for adding during hashing
.C:c00d  85 FB       STA $FB .C:c00f A0 00 LDY #$00           ; offset for reading string
.C:c011  84 FD       STY $FD ; and initial hash value .C:c013 .hashloop: .C:c013 B1 22 LDA ($22),Y        ; read next character from string
.C:c015  10 03       BPL .xor           ; if bit 8 set (shifted)
.C:c017  69 A0       ADC #$A0 ; translate to same unshifted character .C:c019 18 CLC .C:c01a .xor: .C:c01a 45 FD EOR$FD            ; xor with previous hash
.C:c01c  65 FB       ADC $FB ; add offset .C:c01e 85 FD STA$FD            ; store new hash
.C:c020  E6 FB       INC $FB ; increment offset .C:c022 C8 INY .C:c023 C4 FC CPY$FC
.C:c025  D0 EC       BNE .hashloop      ; repeat until last character
.C:c027   .modloop:
.C:c027  E9 29       SBC #$29 ; subtract$29 until
.C:c029  B0 FC       BCS .modloop       ; underflow, then
.C:c02b  69 29       ADC #$29 ; add once again ( => mod$29)
.C:c02d  C9 1A       CMP #$1A ; value in hash range? .C:c02f 90 1C BCC .tochar ; -> output .C:c031 29 0F AND #$0F           ; mask lowest 4 bits only
.C:c033  C9 0D       CMP #$0D ; greater 12 ? .C:c035 90 04 BCC .fixedvals .C:c037 69 09 ADC #$09           ; then just add 10 (9 plus carry)
.C:c039  90 12       BCC .tochar        ; and done -> output
.C:c03b   .fixedvals:
.C:c03b  C9 02       CMP #$02 ; 2 becomes 3 by adding .C:c03d F0 0F BEQ .tochar2 ; with carry (jump after the CLC) .C:c03f C9 08 CMP #$08           ; if value was 8
.C:c041  D0 04       BNE .check2
.C:c043  A9 06       LDA #$06 ; new value is 6 .C:c045 D0 06 BNE .tochar ; and output .C:c046 .check2: .C:c047 C9 0C CMP #$0C           ; else if value was 12
.C:c049  D0 02       BNE .tochar
.C:c04b  A9 11       LDA #$11 ; new value is 17 .C:c04d .tochar: .C:c04d 18 CLC .C:c04d .tochar2: .C:c04e 69 41 ADC #$41           ; add character code for 'a'
.C:c050  4C D2 FF    JMP $FFD2 ; jump to kernal CHROUT routine  Test suite (C64 BASIC, containing the machine code routine in data lines) 0fOa=49152to49234:rEb:pOa,b:nE:pO53272,23 1sY49152,"Aretha Franklin":?":Aretha Franklin" 2sY49152,"Ray Charles":?":Ray Charles" 3sY49152,"Elvis Presley":?":Elvis Presley" 4sY49152,"Sam Cooke":?":Sam Cooke" 5sY49152,"John Lennon":?":John Lennon" 6sY49152,"Marvin Gaye":?":Marvin Gaye" 7sY49152,"Bob Dylan":?":Bob Dylan" 8sY49152,"Otis Redding":?":Otis Redding" 9sY49152,"Stevie Wonder":?":Stevie Wonder" 10sY49152,"James Brown":?":James Brown" 11sY49152,"Paul McCartney":?":Paul McCartney" 12sY49152,"Little Richard":?":Little Richard" 13sY49152,"Roy Orbison":?":Roy Orbison" 14sY49152,"Al Green":?":Al Green" 15sY49152,"Robert Plant":?":Robert Plant" 16sY49152,"Mick Jagger":?":Mick Jagger" 17sY49152,"Tina Turner":?":Tina Turner" 18sY49152,"Freddie Mercury":?":Freddie Mercury" 19sY49152,"Bob Marley":?":Bob Marley" 20sY49152,"Smokey Robinson":?":Smokey Robinson" 21sY49152,"Johnny Cash":?":Johnny Cash" 22sY49152,"Etta James":?":Etta James" 23sY49152,"David Bowie":?":David Bowie" 24sY49152,"Van Morrison":?":Van Morrison" 25sY49152,"Michael Jackson":?":Michael Jackson" 26sY49152,"Jackie Wilson":?":Jackie Wilson" 27dA32,253,174,32,158,173,133,252,32,163,182,169,121,133,251,160,0,132,253,177 28dA34,16,3,105,160,24,69,253,101,251,133,253,230,251,200,196,252,208,236,233 29dA41,176,252,105,41,201,26,144,28,41,15,201,13,144,4,105,9,144,18,201,2,240 30dA15,201,8,208,4,169,6,208,6,201,12,208,2,169,17,24,105,65,76,210,255  Python 2, 68 bytes def f(n):i=hash(n)%337%125%45;return chr(65+i-i/25*2-i/29*21+i/35*2)  Try it online! • interesting to know how did you compose this – Display Name Oct 11 '17 at 13:17 • @SargeBorsch hash(n) computes an unique integer for every names. The modulo operations still keep those ints unique, but lowers their value. The second part (chr(65+i-i/25*2-i/29*21+i/35*2)) is similar to TFelds answer. The modulo operations are bruteforced by a script which I already used here and here. – ovs Oct 11 '17 at 13:34 Javascript, 138 132 characters As all initials are unique, except for MJ = Michael Jackson / Mick Jagger, I check for Michael Jackson specifically (the only one with an h on the 4th position), and for all other names I created a string with the initials followed by a unique letter. s=>s[3]=='h'?'y':"AFaRCbEPcSCdJLeMGfBDgORhSWiJBjPMCkLRlROmAGnRPoMJpTTqFMrBMsSRtJCuEJvDBwVMxJWz".split(s.replace(/[^A-Z]/g,''))[1][0]  Code Snippet Try it here: var solution = s=>s[3]=='h'?'y':"AFaRCbEPcSCdJLeMGfBDgORhSWiJBjPMCkLRlROmAGnRPoMJpTTqFMrBMsSRtJCuEJvDBwVMxJWz".split(s.replace(/[^A-Z]/g,''))[1][0] var testnames = [ "Aretha Franklin", "Ray Charles", "Elvis Presley", "Sam Cooke", "John Lennon", "Marvin Gaye", "Bob Dylan", "Otis Redding", "Stevie Wonder", "James Brown", "Paul McCartney", "Little Richard", "Roy Orbison", "Al Green", "Robert Plant", "Mick Jagger", "Tina Turner", "Freddie Mercury", "Bob Marley", "Smokey Robinson", "Johnny Cash", "Etta James", "David Bowie", "Van Morrison", "Michael Jackson", "Jackie Wilson" ]; testnames.forEach(name=>document.body.append( solution(name) )); • There may be a shorter hashing function, but I like the idea of trying something a human might do. However, I do wish you'd use the code snippet feature instead of linking to JSFiddle. – trlkly Oct 12 '17 at 3:46 • @trlkly I used the code snippet feature now. – nl-x Oct 12 '17 at 8:19 Java (OpenJDK 8), 128126115 113 bytes Not too shabby for a java submission! Thanks to Kevin for saving me a whole lot of bytes with the lambda expressions! s->{int a=0;for(int i:s.substring(1,6).getBytes())a+=i;a*=a==431?0.108:2.65108;return(char)(a==1341?70:a%26+65);}  Try it online! • Nice answer, +1 from me. I'm currently also working on a Java solution by creating a script.. No luck so far though. Btw, you can golf two bytes by changing {a+=i;} to a+=i; – Kevin Cruijssen Oct 10 '17 at 11:28 • @KevinCruijssen Cheers, can't believe I missed that! I wrote a script to try and find the 'magic number' that would give me unique values between 0 and 25 but the best I could do was 24, hence the if statements at the end. – Luke Stevens Oct 10 '17 at 11:35 • Hmm btw, since you are using Java 8, you can also golf char g(String s) to s->. I've modified your TIO to show how to do this, in case you're only used to Java 7 methods. – Kevin Cruijssen Oct 10 '17 at 11:41 • Thanks, I never realised you could do that, I'll update my submission (again!) – Luke Stevens Oct 10 '17 at 11:44 • Hahaha, you can tell I'm new to this – Luke Stevens Oct 10 '17 at 11:49 Python 3, 13299 96 bytes lambda m:chr(98+"ԇ̲ЙГ̫ѼӿИԸՋжʾҍϴЬֺΝעЕΞϾҞ֜ӣ֪".find(chr(sum(map(ord,m)))))  Try it online! Not golfed brilliantly, but I thought I'd give this a go. -33 bytes thanks to modifications made by TFeld. -3 bytes by using find instead of index thanks to ovs. • You can save 6 bytes by using sum(map(ord,m)), also I added Aretha Franklin for 128 bytes – TFeld Oct 10 '17 at 9:19 • And you can use chr(97+...) instead of ascii_lowercase : 99 bytes – TFeld Oct 10 '17 at 9:21 • What about an explanation? – Matsemann Oct 10 '17 at 13:50 • An explanation: sum(map(ord,m)) adds up the ASCII values of the characters in the string m (gives integers in range 702–1506). Then calling chr it converts it to a (Unicode) character with that number: chr(702) is ʾ  to chr(1506) = ע and lots in between. This solution simply looks up this character in a list of all possible (26) such characters to get the index (0–26), then returns character with ASCII code 97 + that index (so 'a' to 'z'). – ShreevatsaR Oct 11 '17 at 0:40 • Your current solution still contains a 99 byte version. You meant to use OVS's version ? – nl-x Oct 11 '17 at 13:17 PHP, 90 88 86 72+1 bytes might become even shorter with a different modulo. <?=strtr(chr(hexdec(substr(md5($argn),0,2))%65+58),"^<adins",bcfgqvx)&_;


Save to file and run as pipe with -nF or try it online.

• Aretha Franklin and Paul McCartney both output W on your test cases, and there is no X output. Don't know if it's an error in the code itself or just the try it online conversion :) – crazyloonybin Oct 10 '17 at 14:12
• @crazyloonybin replacement typo fixed. Thanks for hinting. – Titus Oct 10 '17 at 21:52
• So how do I run this? Your 'try it online' code doesn't use the <?= part. And the 'Run as pipe I'm not getting working. Also, I get notices when trying to run it from command line. – nl-x Oct 11 '17 at 9:35
• @Titus : In CodeGolf you should give the complete function or application that generates (only) the desired output. I'm familiar with <?= ... So my question still is, how do I run your code (from command line)? I fail at feeding it $argn in command line... All my tries so far either give artifacts, and still seem to require more code to get it to run. – nl-x Oct 12 '17 at 13:03 • @nl-x Your PHP yields notices, it´s because you didn´t give it the n option: echo <input> | php -nF <scriptfilename> – Titus Oct 12 '17 at 15:10 Perl, 56, 54, 50, 46 +1 (-p) bytes$=crypt$,DT;/..(.)/;$_=$1;y/01268ADIJNVW/adhilmnpqsux/$=crypt$,DT;/..(.)/;$=$1;y/01268JVW/hlmpqsux/;$=lc

$=crypt$,mO;/..(.)/;$=$1;y/035eft/AHPRXZ/;$=lc Thanks to Dom's comment could save 4 more bytes, also changed to uppercase to fit better requierements. $_=crypt$_,mO;y/035eft/AHPRXZ/;/..(.)/;$_=uc$1  Try It Online • Nice approach! I think you need to write/score a whole callable function though? – Felix Palmen Oct 11 '17 at 12:52 • @FelixPalmen, it's a perl program which can be called inline : perl -pe '$_=crypt$_,mO;/..(.)/;$_=$1;y/035eft/AHPRXZ/;$_=lc' adding link to tio – Nahuel Fouilleul Oct 11 '17 at 13:06
• Nice! Was looking for a similar approach, using $1^$2 but didn't think to use crypt... You can save 4 bytes with some re-ordering: Try it online! – Dom Hastings Oct 11 '17 at 13:17

Python 2, 50 43 bytes

Credit to japh for the new version

lambda n:chr(hash(n)%2354%977%237%54%26+65)


Try it online!

Note: This is dependent on the hash builtin and will not work in all implementations

• 43 bytes: lambda n:chr(hash(n)%2354%977%237%54%26+65) – japh Oct 14 '17 at 10:04
• @japh Nice! My python brute force checker clearly wasn't fast enough ;) – KSab Oct 14 '17 at 10:43

Ruby, 63 bytes

->s{((0x3c4001c151861b27d>>s.sum%98%66).to_s(2).sum%48+64).chr}


Adds the ascii codes of the input, takes them mod 98 and then mod 66 to get a one of 26 unique numbers n in the range 0..65. The huge hexadecimal number contains a 1 bit in each of these 26 places, so by rightshifting it by n we obtain a number with 1..26 1 bits in it. We count the 1 bits by adding the ascii codes and taking mod 48, then add 64 and convert to an ASCII code.

Test program

the map iterates through the singers printing the letter code and singer. It then returns an array of the letter codes, which is sorted to demonstrate that each letter is used once.

f=->s{((0x3c4001c151861b27d>>s.sum%98%66).to_s(2).sum%48+64).chr}
a= [
"Aretha Franklin","Ray Charles",
"Elvis Presley","Sam Cooke",
"John Lennon","Marvin Gaye",
"Bob Dylan","Otis Redding",
"Stevie Wonder","James Brown",
"Paul McCartney","Little Richard",
"Roy Orbison","Al Green",
"Robert Plant","Mick Jagger",
"Tina Turner","Freddie Mercury",
"Bob Marley","Smokey Robinson",
"Johnny Cash","Etta James",
"David Bowie","Van Morrison",
"Michael Jackson","Jackie Wilson"
]

p a.map{|i|p [f[i],i];f[i]}.sort


Output

["S", "Aretha Franklin"]
["E", "Ray Charles"]
["R", "Elvis Presley"]
["J", "Sam Cooke"]
["X", "John Lennon"]
["C", "Marvin Gaye"]
["M", "Bob Dylan"]
["W", "Otis Redding"]
["V", "Stevie Wonder"]
["Y", "James Brown"]
["D", "Paul McCartney"]
["Q", "Little Richard"]
["Z", "Roy Orbison"]
["P", "Al Green"]
["O", "Robert Plant"]
["K", "Mick Jagger"]
["N", "Tina Turner"]
["L", "Freddie Mercury"]
["G", "Bob Marley"]
["I", "Smokey Robinson"]
["A", "Johnny Cash"]
["F", "Etta James"]
["H", "David Bowie"]
["U", "Van Morrison"]
["B", "Michael Jackson"]
["T", "Jackie Wilson"]
["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"]


Octave, 85 83 80 74 bytes

@(n)('A':'Z')(mod(n([1 2 8])*[1;15;47],124)=='#iZo,gp<C&4Y1!8-GKn3wtTIO')


This mess of an anonymous is the result of some messing around in MATLAB trying to find a good way to encode the data.

Basically after a quick analysis, only letters 1,2, and 8 of the input string (smallest string is 8 chars, so we are good) are needed to produce a unique value from each input. The hard part is then converting that unique value into something usable.

MATLAB is terrible at compressing data, so I had to look for another way to make the lookup mapping. I set out trying to find some function on the three input letters that resulted in a unique value that was also a printable ASCII value so that I could embed the mapping in a string of one character per input.

It turns out that matrix multiplying the characters at index [1 2 8] by the integer matrix [1;15;47] and then performing mod 124 results in unique values which all printable ASCII (and none are a ' character which would bugger up string literals). Pleasingly the mapping ends with TIO which is completely accidental. Interestingly this is the only mapping for this equation that gives 26 unique printable ASCII characters.

So basically that is my lookup mapping and calculation. Doing the lookup is simply a case of performing the calculation and comparing with the mapping. Adding on 'A'-1 to the index in the map results in a character A-Z.

You can try it online on TIO, which shows the full mapping of inputs and outputs. For completeness the full mapping is also below:

'Aretha Franklin' ==> B
'Ray Charles'     ==> S
'Elvis Presley'   ==> F
'Sam Cooke'       ==> V
'John Lennon'     ==> L
'Marvin Gaye'     ==> N
'Bob Dylan'       ==> C
'Otis Redding'    ==> Q
'Stevie Wonder'   ==> X
'James Brown'     ==> I
'Paul McCartney'  ==> R
'Little Richard'  ==> M
'Roy Orbison'     ==> T
'Al Green'        ==> A
'Robert Plant'    ==> U
'Mick Jagger'     ==> P
'Tina Turner'     ==> Y
'Freddie Mercury' ==> H
'Bob Marley'      ==> D
'Smokey Robinson' ==> W
'Johnny Cash'     ==> K
'Etta James'      ==> G
'David Bowie'     ==> E
'Van Morrison'    ==> Z
'Michael Jackson' ==> O
'Jackie Wilson'   ==> J


• Saved 2 bytes tweaking mapping to remove +32.
• Saved 3 bytes by making Octave-only using logical indexing of 'A':'Z' rather than find.
• Saved 6 bytes by doing sum of multiplications using matrix multiplication.
• Clever approach, maybe too clever? 53 bytes, based on my MATL answer – Sanchises Oct 12 '17 at 6:47
• @Sanchises possibly, but it was the approach I came up with ¯_(ツ)_/¯. You're welcome to post your version as a separate answer. – Tom Carpenter Oct 12 '17 at 7:25
• I figured, and I agree that it's more interesting to have different approaches than just copy the shortest approach. I just wanted to compare the approaches, I think yours is more clever but I suppose the dataset just allowed for an easy mod-based approach (not sure if that's a coincidence or statistically likely) – Sanchises Oct 12 '17 at 8:12

JavaScript (Chrome), 102

Note Unfortunately it works only in Chrome, because of implementation-dependent approximations in parseInt() (thanks @Arnauld)

s=>s[0]=='L'?'z':"ab.c..defghijklmn...o..pqrst.u.vwxy"[parseInt(s.replace(/ /,'o').slice(0,14),36)%35]


I looked for a hash function, taking a slice of each name, converting to numbers using base 36 and then applying a modulo.

I used this code to look for the best hash:

x=Al Green\nAretha Franklin\nBob Dylan\nBob Marley\nDavid Bowie\nElvis Presley\nEtta James\nFreddie Mercury\nJackie Wilson\nJames Brown\nJohn Lennon\nJohnny Cash\nLittle Richard\nMarvin Gaye\nMichael Jackson\nMick Jagger\nOtis Redding\nPaul McCartney\nRay Charles\nRobert Plant\nRoy Orbison\nSam Cooke\nSmokey Robinson\nStevie Wonder\nTina Turner\nVan Morrison.split(\n)

max=0
for(m=26;m<60;m++)
for(i=0;i<20;i++)
for(j=i;++j<20;)
for(k=0;k<37;k++)
{
S=new Set();
f=k ? (k-1).toString(36) : ''
if (S.size > max) console.log(i,j,f,m,max=S.size)
}


And the results:

0 1 "" 26 14
0 2 "" 26 15
0 4 "" 26 16
0 5 "" 26 18
0 6 "0" 26 19
0 6 "3" 26 20
0 8 "a" 26 21
2 5 "7" 28 23
0 14 "h" 35 24
0 14 "o" 35 25
2 9 "" 51 26


The best hash function gives 26 different values between 0 and 50, but I used a different one, with 1 duplicate but a smaller range.

Test

var names=x=Al Green\nAretha Franklin\nBob Dylan\nBob Marley\nDavid Bowie\nElvis Presley\nEtta James\nFreddie Mercury\nJackie Wilson\nJames Brown\nJohn Lennon\nJohnny Cash\nLittle Richard\nMarvin Gaye\nMichael Jackson\nMick Jagger\nOtis Redding\nPaul McCartney\nRay Charles\nRobert Plant\nRoy Orbison\nSam Cooke\nSmokey Robinson\nStevie Wonder\nTina Turner\nVan Morrison.split(\n)

var F=
s=>s[0]=='L'?'z':"ab.c..defghijklmn...o..pqrst.u.vwxy"[parseInt(s.replace(/ /,'o').slice(0,14),36)%35]

var Singers={}
names.forEach(n=>Singers[F(n)]=n)

;Object.keys(Singers).sort().forEach(i=> console.log(i, Singers[i]))

• You may want to mention that it only works on Chrome because of implementation-dependent approximations in parseInt(). – Arnauld Oct 11 '17 at 15:31
• @Arnauld thanks, I did not know. – edc65 Oct 11 '17 at 19:27

C, 6555 49 bytes

h(char*s){return*s<77?(*s^s[5]+40)%13+65:(s[5]&s[4]+s[1])%13+78;}

h(char*s){return*(long*)s%887%392%224%120%67%40%26+65;}

Same approach as KSab's answer. C doesn't provide a string hash function like Python. Or does it?

h(char*s){return*(long*)s%2004%857%361%94%26+65;}


Try it online!

h returns an int whose values are the ASCII codes for A .. Z.

Javascript, 98 bytes

s=l=>("heCysvCm hirDb iiesm ultOyr rb c ndeMbeonh tdvMnacic".indexOf(l[4]+l[2])/2+10).toString(36)


I found that the combination of the 2nd and the 4th character of the names is unique for each of them.

Therefore I create a string with the combinations of name[4] + name[2], not name[2] + name[4] or I would have a repetition of the char group eh of the first name Aretha Franklin eh and when Smokey Robinson and Johnny Cash oehn are concatenated.

I could just move Johnny Cash to another position of the string and get a different mapping, but concatenating the 4th and the 2nd character in this order avoids the collision and leaves the dataset order intact without adding more length to the solution. So I decided to go that way (it's just personal preference)

I search for the position of the concatenation of the 4nd and 2nd letter of the given parameter in the string and divide it by 2 so that I get a number between 0 and 25. Then I add 10 and convert it to string from base 36, where 10 corresponds to a and 35 to z

let singers = [
"Aretha Franklin",
"Ray Charles",
"Elvis Presley",
"Sam Cooke",
"John Lennon",
"Marvin Gaye",
"Bob Dylan",
"Otis Redding",
"Stevie Wonder",
"James Brown",
"Paul McCartney",
"Little Richard",
"Roy Orbison",
"Al Green",
"Robert Plant",
"Mick Jagger",
"Tina Turner",
"Freddie Mercury",
"Bob Marley",
"Smokey Robinson",
"Johnny Cash",
"Etta James",
"David Bowie",
"Van Morrison",
"Michael Jackson",
"Jackie Wilson"
]

s=l=>("heCysvCm hirDb iiesm ultOyr rb c ndeMbeonh tdvMnacic".indexOf(l[4]+l[2])/2+10).toString(36)

singers.forEach(singer => console.log(s(singer), singer))

• very cleaver the combination you found! – Joyal Oct 12 '17 at 13:05
• aaah yes. off course. i have something quite similar, but instead of adding the char to return in the string, the position of the occurance could save 25 chars. Clever! – nl-x Oct 12 '17 at 16:08

Wolfram Language (Mathematica), 101 126 bytes

Alphabet[][[#&@@#&@@Position[(t=ToCharacterCode)@"nZ-Be_;f%d^q 0w@x~KDaLJ&k",Total@t@#~Mod~98+32]]]&


+32 seems leading to the shortest stirng hashtable in Mathematica InputForm.

Try it online!

///, 390 231 bytes

/gg/U/
/a///b///c///d///e///f///g///h///i///j///k///l///m///n///o///p///q///r///s///t///u///v///w///x///y///z//
/ F//
/A //
/ G//
/ M//
/M //
/J B/X/
/J //
/L //
/ R//
/R C/Y/
/S C/Z/
/B D/K/
/ B//
/ J//
/ T//
/S W/I/
/R O/N/
/JU/U/
/R //
/E P/H/
/PC/Q/


Try it online!

231 bytes after removing the newlines.

This is very long, but /// cannot handle different characters generically. In other words, /// does not support regex.

Excel, 96 bytes

After too much time wasted trying other approaches, have implemented @Eduardo Paez's approach:

=CHAR(FIND(MID(A1,5,1)&MID(A1,3,1),"her DbMbdvsv tdeicsm hnhltirac c i uCyrbOyCmeoie nMn")/2+65)
`