32
\$\begingroup\$

A comment I made in chat and the ensuing conversation inspired me to make this challenge.

Am I the only one referred to by initials around here? We are all about golfing things down. We can have MB and D-nob and ... O.

If I'm known as "CH" then I think everyone else ought to have an initial-based nickname as well.

Here's a list of the top 100 Programming Puzzles & Code Golf users by reputation to play with:

Martin Büttner
Doorknob
Peter Taylor
Howard
marinus
Dennis
DigitalTrauma
David Carraher
primo
squeamish ossifrage
Keith Randall
Ilmari Karonen
Quincunx
Optimizer
grc
Calvin's Hobbies
ugoren
Mig
gnibbler
Sp3000
aditsu
histocrat
Ventero
xnor
mniip
Geobits
J B
Joe Z.
Gareth
Jan Dvorak
isaacg
edc65
Victor
steveverrill
feersum
ace
Danko Durbić
xfix
PhiNotPi
user23013
manatwork
es1024
Joey
daniero
boothby
nneonneo
Joey Adams
Timwi
FireFly
dansalmo
grovesNL
breadbox
Timtech
Flonk
algorithmshark
Johannes Kuhn
Yimin Rong
copy
belisarius
professorfish
Ypnypn
trichoplax
Darren Stone
Riot
ProgramFOX
TheDoctor
swish
minitech
Jason C
Tobia
Falko
PleaseStand
VisioN
leftaroundabout
alephalpha
FUZxxl
Peter Olson
Eelvex
marcog
MichaelT
w0lf
Ell
Kyle Kanos
qwr
flawr
James_pic
MtnViewMark
cjfaure
hammar
bitpwner
Heiko Oberdiek
proud haskeller
dan04
plannapus
Mr Lister
randomra
AShelly
ɐɔıʇǝɥʇuʎs
Alexandru
user unknown

(this is how I got it)

Challenge

Write a program or function that takes in a list of strings and outputs another list of strings of their minimal, unique, initial-based nicknames, giving preference to those closer to the start of the list.

Apply this method to each string S in the list in the order given to create the nicknames:

  1. Split S up into words separated by spaces, removing all spaces in the process.
  2. List the nonempty prefixes of the string of the first letters of the words in S, from shortest to longest.
    e.g. Just Some NameJ, JS, JSN
  3. Choose the first item in this list that is not identical to an already chosen nickname as the nickname for S. Stop if a nickname was chosen, continue to step 4 otherwise.
    e.g. if Just Some Name was the first string then J is guaranteed to be the nickname.
  4. List the prefixes again, but this time include the second letter of the first word in it's natural place.
    e.g. Just Some NameJu, JuS, JuSN
  5. Do the same as in step 3 for this list, stopping if a unique nickname is found.
  6. Repeat this process with the remaining letters of the first word, eventually inserting letters into the second word, then the third, and so on, until a unique nickname is found.
    e.g. The first unique string listed here will be the nickname:
    Jus, JusS, JusSN
    Just, JustS, JustSN
    Just, JustSo, JustSoN (note that o was not added after Just)
    Just, JustSom, JustSomN
    Just, JustSome, JustSomeN
    Just, JustSome, JustSomeNa
    Just, JustSome, JustSomeNam
    Just, JustSome, JustSomeName

In the end all the input strings should end up with a unique nickname (potentially identical to the string). You may assume that none of the input strings will map to the same nickname using this method.

Example

Updated to fix my mistake!

For the input

Martin Buttner
Doorknob
Peter Taylor
Howard
marinus
Dennis
DigitalTrauma
David Carraher
Martin Bitter
Martin Butter
Martin Battle
Martini Beer
Mart Beer
Mars Bar
Mars Barn

the nicknames would be

M
D
P
H
m
De
Di
DC
MB
Ma
MaB
Mar
MarB
Mars
MarsB

Details

  • Input can be from a file (one name per line), or one name at a time via stdin/command line, or as a function argument of a list of strings, or as a function arg of a single string with newlines between names.
  • Output should be printed to stdout (one nickname per line) or returned by the function as a list of strings, or as one string with newlines between nicknames.
  • Ideally programs will work for names that contain any characters except line terminators. However, you may assume that all the names only contain printable ASCII. (The PPCG names don't.)
  • Only the regular space character counts as a word separator. Leading and trailing spaces can be ignored.

Scoring

The shortest submission in bytes wins. Tiebreaker goes to the answer posted earliest.

\$\endgroup\$
  • 49
    \$\begingroup\$ This explains why I woke up in the middle of the night with this vague sensation of feeling violated. \$\endgroup\$ – Martin Ender Feb 27 '15 at 9:59
8
\$\begingroup\$

CJam, 58 53 bytes

This can be golfed a lot.. But for starters:

LqN/{:Q1<aQ,,:)QS/f{{1$<_,@-z1e>}%W<s}+{a1$&!}=a+}/N*

Code Expansion:

L                         "Put an empty array on stack. This is the final nickname array";
 qN/{  ...   }/           "Read the input and split it on new lines. Run the block for each";
     :Q1<a                "Store each name in Q and get its first char. Wrap it in an array";
          Q,,:)           "Get an array of 1 to length(name) integers";
               QS/        "Split the name on spaces";
f{{           }%   }      "for each of the integer in the array, run the code block";
                          "and then for each of the name part, run the inner code block";
   1$<                    "Copy the integer, take first that many characters from the";
                          "first part of the name";
      _,@-z1e>            "Get the actual length of the part and the number of characters";
                          "to be taken from the next name part, minimum being 1";
                W<        "Get rid of the last integer which equals 1";
                  s       "Concat all name parts in the array";
                    +     "Add the list of nick names as per spec with the first character";
{     }=                  "Get the first nick name that matches the criteria";
 a1$&                     "Wrap the nick name in an array and do set intersection with";
                          "the copy of existing nick names";
     !                    "Choose this nick name if the intersection is empty";
N*                        "After the { ... }/ for loop, the stack contains the final";
                          "nick names array. Print it separated with new lines";

Try it online here

\$\endgroup\$
  • 2
    \$\begingroup\$ See my comment to OP: if 'Ju' or 'Jus' are valid nicknames for 'Just Some Name', 'Maertin Butter' sholud be 'Ma', then 'MaB','Mar','MarB'. \$\endgroup\$ – edc65 Feb 27 '15 at 14:59
  • \$\begingroup\$ What @edc65 says is true. Sorry about that. I'll allow you to not change things if you want; it was my mistake. \$\endgroup\$ – Calvin's Hobbies Feb 27 '15 at 21:58
9
\$\begingroup\$

JavaScript (ES6) 159

Following the specs and not the example.

I generate the candidate nicknames having a current middle word (at the beginning, the first word). The words before the current are used 'as is'. The words after the current contribute with no - or just the first - character. The current word contributes with 1 more character for each loop.

Example 'Just Some Name' => 'Just','Some','Name'
Cw Just, position 1, try J, JS, JSN
Cw Just, position 2, try Ju, JuS, JuSN
Cw Just, position 3, try Jus, JusS, JusSN
Cw Just, position 4, try Just, JustS, JustSN
Now Just is exhausted, Some becomes Cw, position restarted to 2 (for position 1, all already tried)

Cw Some, position 2, try Just, JustSo, JustSoN
Cw Some, position 3, try Just, JustSom, JustSomN
Cw Some, position 4, try Just, JustSome, JustSomeN
Now Some is exhausted, Name becomes Cw, position restarted to 2

Cw Name, position 2, try Just, JustSome, JustSomeNa
Cw Name, position 3, try Just, JustSome, JustSomeNam
Cw Name, position 4, try Just, JustSome, JustSomeName
That's all folks!

The code

(q is current word position, p is slicing position)

F=l=>
  l.map(w=>{ 
    for(w=w.match(/[^ ]+/g),q=p=0;
        w.every((w,i)=>~o.indexOf(t+=i<q?w:i>q?w[0]:w.slice(0,p+1)),t='')
        &&(w[q][p++]||(p=1,w[++q]));
       );
    o.push(t)
  },o=[])&&o

Test In Firefox/FireBug console

F(['Martin Buttner','Doorknob','Peter Taylor','Howard','marinus'
  ,'Dennis','DigitalTrauma','David Carraher'
  ,'Martin Bitter','Martin Butter','Martin Battle','Martini Beer','Mart Beer'])

["M", "D", "P", "H", "m", "De", "Di", "DC", "MB", "Ma", "MaB", "Mar", "MarB"]

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

PHP, 327 289 275 274 270

There may still be a little golfing potential.

while($n=fgets(STDIN)){$c=count($w=preg_split('/\s+/',trim($n)));$p=[];for($k=0;$k<$c;$p[$k]++){for($t='',$j=0;$j<$c;$j++)$t.=substr($w[$j],0,$p[$j]+1);for($j=1;$j<=strlen($t);$j++)if(!in_array($v=substr($t,0,$j),$u))break 2;$k+=$p[$k]==strlen($w[$k]);}echo$u[]=$v,'
';}
  • Program operates on stdin/stdout, works on ASCII, buggy on UTF
  • usage: php -d error_reporting=0 golfnicks.php < nicknames.txt
  • or cat <<EOF | php -d error_reporting=0 golfnicks.php + list of names + EOF
  • To test as function in web browser: fetch the breakdown, uncomment all lines marked with // FUNC and comment the one marked with //PROG. Try f(array_fill(0,21,'Just Some Name'));

breakdown

#error_reporting(0);function f($a){echo'<pre>'; // FUNC
#foreach($a as$n) // FUNC
while($n=fgets(STDIN)) // PROG
{
    $c=count($w=preg_split('/\s+/',trim($n)));     // split name to words, count them
    $p=[];                                         // initialize cursors
    for($k=0;$k<$c;$p[$k]++)
    {
        for($t='',$j=0;$j<$c;$j++)$t.=substr($w[$j],0,$p[$j]+1); // concatenate prefixes
        for($j=1;$j<=strlen($t);$j++)              // loop through possible nicks
            if(!in_array($v=substr($t,0,$j),$u))   // unused nick found
                break 2;                           // -> break cursor loop
        $k+=$p[$k]==strlen($w[$k]);                // if Cw exhausted -> next word
        // strlen()-1 would be correct; but this works too)
    }
    echo$u[]=$v,'
';
}
#echo '</pre>';} // FUNC
\$\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.