Take the 2-minute tour ×
Programming Puzzles & Code Golf Stack Exchange is a question and answer site for programming puzzle enthusiasts and code golfers. It's 100% free, no registration required.

Given a string of unsorted alphanumeric characters, e.g.

ABC321STPpJqZZr0

output a ", "-separated list of character ranges, sorted by ASCII value, ignoring case and removing duplicates (i.e. outputting only uppercase and numeric characters), e.g.

0-3, A-C, J, P-T, Z

Rules

  • The length of your program is your base score, as usual.
  • You must initialize (hardcode) the above example within your program, but you may discount the length of that example from your program length, e.g. for char* s="ABC321STPpJqZZr0"; you may discount 16 chars, the other 11 chars counting toward your program length.

Bonus (+50 bounty)

  • As this was a real problem encountered by my coworker today, needing to be written in Tcl 8.0.5 (an ancient version, lacking many of the latest Tcl built-ins), I'll award 50 points to whomever writes the shortest Tcl 8.0.5 solution, if there are at least 2 valid submissions in Tcl 8.0.5.
share|improve this question
    
@FezVrasta - I intentionally wrote ", " to include the space, but we can leave your edit and let this comment serve as that indication. –  Andrew Cheong Feb 11 at 0:19
    
Why intentionally include GolfScript? Why not allow other languages, such as Befunge? –  Quincunx Feb 11 at 1:14
    
We can say all is fair game. I just won't be able to check most of them very easily. –  Andrew Cheong Feb 11 at 1:15
    
So is that a Code-Golf tag then? –  VisioN Feb 11 at 8:35
1  
@Chron - Good catch. A-B in my case but since there have already been many submissions, let's allow both ways. –  Andrew Cheong Feb 11 at 9:21
add comment

10 Answers

up vote 4 down vote accepted

Ruby, 87-16=71

EDIT: Had to add some characters so that two-character ranges are displayed correctly. Also using ?[ instead of ?Z to fix a bug with ranges ending in Z.

$><<[*?0..?[].join.gsub(/[^ABC321STPpJqZZr0]/i,$/).gsub(/\B.+\B/,?-).scan(/.-.|./)*', '

You can see the Ideone run here.

share|improve this answer
    
+1 For one-liner. Very clever use of various methods; This is really brilliant. –  daniero Feb 11 at 4:12
1  
Note that gsub(/[]/i) is shorter than tr(''.upcase) by 2 characters. Moreover, scan(/.+/) -> split saves 5, and $><< instead of puts another one. –  Howard Feb 11 at 7:36
    
@Howard Great suggestions, thanks! –  Chron Feb 11 at 9:15
add comment

GolfScript 57 54 52

 'ABC321STPpJqZZr0'
 {.95>32*-}%.|:x..{(}%&-x..{)}%&-+$2/{.|'-'*}%', '*

Try it here.

The code first capitalizes everything:

{.95>32*-}%

Then gets unique characters and saves it in a variable:

.|:x

Then, we get the characters whose direct predecessors are not in the string (so that they are the beginning part of a range):

..{)}%&-x

We similarly get the ends of ranges with x..{)}%&-.

Now actually form the ranges by concatenating the lists, sorting, and splitting into groups of 2:

+$2/

The rest is just formatting, using * as string join.

share|improve this answer
1  
In the output, the ranges need to be separated by ', ' and not just ',' –  Chron Feb 11 at 1:30
1  
Also .95>{32-}{}if -> .95>32*- saves 5 characters. –  Howard Feb 11 at 5:29
    
@Howard Great! I knew that part was suboptimal. –  Ben Reich Feb 11 at 5:47
1  
@Chron Fixed the space issue! –  Ben Reich Feb 11 at 5:47
add comment

Julia, 131

julia> l=sort(unique(uppercase("ABC321STPpJqZZr0")))
julia> prod([!(c+1 in l)?"$c"*(c==l[end]?"":", "):!(c-1 in l)?"$c":(c+1 in l)&&!(c+2 in l)?"-":"" for c in l])

"0-3, A-C, J, P-T, Z"

Not supported by Ideone.com, and will probably be crushed anyway.

share|improve this answer
1  
Thanks anyway! The Ideone.com constraint was only so I could test it but I suppose I can trust the integrity of golfers and remove that rule. +1, anyway. –  Andrew Cheong Feb 10 at 23:10
add comment

Python 2.x, 304 - 16 = 288

This can surely be golfed further, all comments welcome!

e=[""]*11;f=[""]*27
for c in"ABC321STPpJqZZr0".lower():e["0123456789".find(c)]=f["abcdefghijklmnopqrstuvwxyz".find(c)]=c
e[-1]=f[-1]=""
def h(j):
 g=[];k=l=i=0
 for e in j:
  if e:
   if not l:k=i;l=1
  elif l:l=g.append((k,i-1))
  i+=1
 print", ".join([j[m],j[m]+"-"+j[n]][n-m>1]for m,n in g)
h(e);h(f)
share|improve this answer
add comment

C#, 221 bytes

class P{
    static void Main(){
        var s="ABC321STPpJqZZr0";
        var l=new int[257];
        foreach(int c in s.ToUpper())
            l[c]=1;
        var r="";
        for(int i=0;i<255;){
            if(l[i++]-l[i]<0)
                r+=", "+(char)i;
            else if(l[i+1]-l[i]<0)
                r+="-"+(char)i;
        }
        System.Console.Write(r.Substring(2));
    }
}
share|improve this answer
add comment

C, 193

char*s="ABC321STPpJqZZr0";
int c[99];memset(c,0,396);while(*s){++c[toupper(*s++)];}for(int i=0,f=1,r=0;
i<=99;++i){if(!r&&c[i])r=i;if(r&&!c[i]){if(!f)printf(", ");putchar(r);
if(i-r>1)printf("-%c",i-1);r=f=0;}}
share|improve this answer
    
Can you add a small explanation? –  Quincunx Feb 11 at 6:42
    
Iterate over the string, accumulating a count of instances of each alphanumeric character. Then, iterate over all alphanumeric characters in alphabetical order, writing out the start of each compact range and, if appropriate, a dash followed by the end of the range. If this is not the first range that's been written, add the comma-space separator. Code must be embedded in a main() function with appropriate headers (stdio, string, ctypes) included, so I kinda cheated there. –  warrenm Feb 11 at 7:25
add comment

Q, 94

{","sv(,/){{"-"sv(?) -1 1#\:x}'[cut[;a]0,1_(&)1<(-':)"i"$'a:asc upper[x]inter y]}[x]'[.Q`n`A]}
share|improve this answer
add comment

Rebol (218 - 16 = 202)

m: s: sort uppercase unique"ABC321STPpJqZZr0"i: :to-integer f: does[either 1 = length? x: copy/part m s[x][rejoin[x/1"-"last x]]]while[not tail? s: next s][if(1 + i pick back s 1)!=(i s/1)[prin join f", "m: s]]print f

Non-minified version:

m: s: sort uppercase unique "ABC321STPpJqZZr0"
i: :to-integer

f: does [
    either 1 = length? x: copy/part m s [x] [rejoin [x/1 "-" last x]]
]

while [not tail? s: next s][
    if (1 + i pick back s 1) != (i s/1) [
        prin join f ", "
        m: s
    ]
]

print f
share|improve this answer
add comment

q [116 chars]

{.a:();{m:6h$x;.a:.a,$[m[1]=1+m[0];45;m[0],44,m 1];1_x}/[x:asc distinct upper x];p where differ 6h$p:-3_10h$x[0],.a}

Usage

{.a:();{m:6h$x;.a:.a,$[m[1]=1+m[0];45;m[0],44,m 1];1_x}/[x:asc distinct upper x];p where differ 6h$p:-3_10h$x[0],.a}"ABC321STPpJqZZr0"
Output
"0-3,A-C,J,P-T,Z"

There is a scope of saving chars, i'll try some other method and post it.

share|improve this answer
add comment

Tcl 8.0.5, 344 (360 bytes)

set a ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789
set s string
set x [join [lsort [split [$s toupper ABC321STPpJqZZr0] ""]] ""]
regsub -all (.)\\1+ $x \\1 x
set i 36
while {[incr i -1]} {set j -1
while {$i+[incr j]<36} {set y [$s range $a $j [expr $i+$j]]
regsub $y $x [$s index $y 0]-[$s index $y end],\  x}}
while {[regsub -all {(\w)(\w)} $x {\1, \2} x]} {}
puts $x

Tcl 8.0.5, 340 (356 bytes)

Tinkering with the rename command yielded some fun tricks! I've documented them in another thread.

rename rename &
& set =
& regsub R
& string S
& while W
= a ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789
= x [lsort [split [S toupper ABC321STPpJqZZr0] ""]]
R -all {(.) \1+| } $x \\1 x
= i 36
W {[incr i -1]} {= j -1
W {$i+[incr j]<36} {= y [S range $a $j [expr $i+$j]]
R $y $x [S index $y 0]-[S index $y end],\  x}}
W {[R -all {(\w)(\w)} $x {\1, \2} x]} {}
puts $x

Tcl 8.0.5, 332 (348 bytes) [Unstable—depends on $PATH]

info script ""
set tcl_interactive 1
set a ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789
set x [lso [sp [st toupper ABC321STPpJqZZr0] ""]]
regs -all {(.) \1+| } $x \\1 x
set i 36
wh {[inc i -1]} {set j -1
wh {$i+[inc j]<36} {set y [st range $a $j [exp $i+$j]]
regs $y $x [st index $y 0]-[st index $y end],\  x}}
wh {[regs {(\w)(\w)} $x {\1, \2} x]} {}
pu $x

Credit to @JohannesKuhn for the interactive trick.

share|improve this answer
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.