Make a lookup index

Given a string, return a table where the first column has the unique letters of the string in order of occurrence and subsequent columns list the indices of that letter in the string, using zero or one-based indexing. Horizontal whitespace does not matter, as long as the left-most column is vertically aligned. Indices must be in ascending order from left to right.

Examples

Using zero-based indexing and given "abracadabra", return

a 0 3 5 7 10
b 1 8
r 2 9
c 4
d 6


Using one-based indexing and given "3141592653589793238462643383279503", return:

3  1 10 16 18 25 26 28 34
1  2  4
4  3 20 24
5  5  9 11 32
9  6 13 15 31
2  7 17 22 29
6  8 21 23
8 12 19 27
7 14 30
0 33

• Can I have leading spaces in the output? – Erik the Outgolfer Jun 20 '17 at 13:20
• Must the output format be strict? – Leaky Nun Jun 20 '17 at 13:21
• Does this need to work for characters that aren't printable ascii? Are there any characters we can assume won't be in the string (particularly whitespace)? – FryAmTheEggman Jun 20 '17 at 16:18

APL (Dyalog), 4 bytes

,⌸


Try it online!

(⌸ is 3 bytes)

Uses 1-based indexing.

⌸ is the key operator. Here it behaves as a monadic operator. It applies the function ,, concatenate the left argument with the right argument, to each unique element in its right argument and the indices of that unique element in the original argument.

• Hm, this needs an explanation :-) – Adám Jun 20 '17 at 13:53
• @Adám I suspect this challenge had been tailored specifically for APL 😉 – Uriel Jun 20 '17 at 14:30
• @Uriel The other way around. I was looking at what APL can do neatly, and thought that this would make a good challenge. – Adám Jun 20 '17 at 14:32
• I see 4 bytes there. – Adám Jun 20 '17 at 14:32

import Data.List
f s=unlines[x:concat[' ':show i|i<-[0..length s-1],s!!i==x]|x<-nub s]


Defines a function, f, which returns a String containing this output.

Try it online!

How?

import Data.List                                                            -- Imports Data.List. This contains the nub method which is necessary
f s =                                                                       -- Define a function, f, which takes one argument, s
[                                               |x<-nub s]      -- Loop through nub s with the variable x. Nub removes duplicates from a list, in this case the input.
[          |i<-[0..length s-1]        ]                -- Go thourgh the list [0,1,2...] until the length of the input - 1. Basically a indexed for-loop
,s!!i==x                 -- Filter out everything where the char at this index isn't x
' ':show i                                            -- i as a string with a space before it
concat                                                       -- Flatten the whole list
x:                                                             -- Append x before it
unlines                                                                -- Insert newlines between every element in the list and flatten it, effectively putting every element on it's own line

• I don't think I've ever seen that comment format used for Haskell. – Adám Jun 20 '17 at 13:53
• Your inner list comprehension can be shortened to [' ':show i|(i,c)<-zip[0..]s,c==x]. – Laikoni Jun 20 '17 at 15:47

kdb+/q, 5 bytes

group


Builtins are fab

q)group"abracadabra"
a| 0 3 5 7 10
b| 1 8
r| 2 9
c| ,4
d| ,6


I usually golf in k, but the 2-byte k version (=:) doesn't format the output nicely

k)=:"abracadabra"
"abrcd"!(0 3 5 7 10;1 8;2 9;,4;,6)


The results are exactly the same, but the formatting is lost. To format, and to remove the return object, we actually pick up more bytes than the q version

k)f:{1@.Q.s x;} //11 bytes!
a| 0 3 5 7 10
b| 1 8
r| 2 9
c| ,4
d| ,6

• Hm, it returns a dictionary. Interesting. – Adám Jun 20 '17 at 15:37

Python, 96 bytes

def f(s,r=[]):
for c in s:
if c not in r:print(c,*(i for i,l in enumerate(s) if l==c));r+=[c]


try it online!

MATL, 11 bytes

u"@0hG@=fVh


Output indices are 1-based.

Try it online!

Pyth, 13 bytes

jmj;+dxCdCMQ{


Try it online!

Jelly, 7 bytes

Q;"ĠṢ$G  Try it online! 05AB1E, 14 bytes ÙvSyQƶ0Ky¸ìðý,  Try it online! Explanation Ùv # for each unique char y in input S # split input into a list of chars yQ # compare each to y for equality ƶ # multiply each by its 1-based index 0K # remove zeroes y¸ì # prepend y to the list of indices ðý, # join by spaces and print  Mathematica, 85 bytes using one-based indexing (t=#;j=First/@t~StringPosition~#&/@(s=First/@Tally@Characters@t);Row[Column/@{s,j}])&  JavaScript (ES Draft), 77 bytes s=>s.replace(/./g,(c,i)=>a[c]=(a[c]||c)+ +i,a={})&&Object.values(a).join   88 bytes in older browsers: f= s=>s.replace(/./g,(c,i)=>a[c]=(a[c]||c)+ +i,a={})&&Object.keys(a).map(c=>a[c]).join  <input oninput=o.textContent=f(this.value)><pre id=o> PHP, 82 bytes for(;~$c=$argn[$i];)$r[$c].=str_pad(++$i,5," ",0);foreach($r as$k=>$v)echo"$k$v
";


Try it online!

QBIC, 95 bytes

dim X(126)[_l;||i=asc(_sA,a,1|)┘X(i)=X(i)+@ +!a$][_lA||_SA,b,1|i=asc(C)~X(i)<>D|?C,X(i)┘X(i)=@  Explanation This copies significant parts of my answer on this challenge: dim x(126) Create an array of 126 elements (one for each ASCII element) [_l;|| Read cmd line input, loop over its length i=asc(_sA,a,1|) Read the next char's ascii value ┘ (Syntactic linebreak) X(i)= Set the value for this ascii-codepoint to X(i) everything we've put in before +@  and a literal space +!a$             and the current (1-based) index cast as string
]              close the FOR loop
[_lA||         Loop over the original string again (to preserve order of appearance)
_SA,b,1|       Read 1 char, assign to C$(note the capital S in the function call, this auto-creates C$ abd assigns it the value of the substring-call)
i=asc(C)       Get the index in our storage array from C$'s ascii value ~X(i)<>D IF the storage array holds data for i (<> D$, which we'll set to "" in a second),
|?C,X(i)       THEN PRINT the character, followed by the indices saved for this char
┘              (Syntactic linebreak)
X(i)=@         Clear out the data stored for C$@ declares a string lit,  would close it and that gets auto-added at EOF, creating the literal @, which gets assigned to D$


Sample run:

Command line: abracadabra
a              1 4 6 8 11
b              2 9
r              3 10
c              5
d              7


C (clang), 176 bytes

G(S,V,c,g,i,H,L)char*S,*V;{for(V=malloc(8),H=strlen(S),c=g=-1;++g<H;){if(strchr(V,L=S[g]))continue;printf("%c ",V[c++]=L);for(i=-1;++i<H;printf(S[i]==L?"%d ":"",i));puts("");}}


Of course this is the longest answer here...

Try it online!

• Where is the Java solution? – Adám Jun 21 '17 at 6:35
• 164 bytes – ceilingcat Jul 31 '19 at 5:19

Python 3, 111 106 bytes

def q(f):
d={a:[]for a in f}
for a,b in enumerate(f):d[b]+=[a]
[print(p,*d.pop(p))for p in f if p in d]


repl.it

C#, 138 bytes

using System.Linq;s=>Console.Write(string.Join("\n",s.Distinct().Select(c=>c+string.Join("",s.Select((d,i)=>d==c?i:-1).Where(i=>i>-1)))));


F#, 120 bytes

let f s=s|>Seq.indexed|>Seq.groupBy(fun(a,b)->b)|>Seq.iter(fun(a,b)->
printf"\n%c"a
Seq.iter(fun(c,_)->printf"%i"c)b)


let f s =
s
|> Seq.indexed
|> Seq.groupBy (fun (idx, char) -> char)
|> Seq.iter (fun (char, charIdxSeq) ->
printf "\n%c" char
Seq.iter (fun (idx, _) -> printf "%i" idx) charIdxSeq)


Seq.indexed creates a new sequence containing tuples which are composed of the original element and it's 0 based index in the original sequence.

The rest is fairly self explanatory which is never a good thing for golf!

R, 80 77 bytes

function(s)for(i in (z=unique(s<-strsplit(s,'')[[1]])))cat(i,which(s==i),"
")


Try it online!

an anonymous function; prints the result 1-indexed with a trailing newline.

J, 11 bytes

~.,.":@I.@=


Try it online!

Husk, 10 bytes

§mȯ§:;ms¥u


Try it online!

Note: Husk (or at least the command ¥) is newer than this challenge.

Explanation

§mȯ§:;ms¥u  Implicit input, say S = "ababbc".
u  Remove duplicates: "abc"
§m          Map over this string:
Argument is a character, say 'b'.
¥    1-based indices in S: [2,4,5]
ms     Convert each to string: ["2","4","5"]
;       Wrap argument in a string: "b"
ȯ§:        Prepend to list of index strings: ["b","2","4","5"]
Result is a list of lists of strings
[["a","1","3"],["b","2","4","5"],["c","6"]]
Implicitly print, separating strings by spaces and lists by newlines.