Given a string, find the 10 least common alpha-numeric characters within that string.

Output should be formatted with numbers left-padded with spaces so they look like this:

e:      1
t:     16
c:     42
b:    400
z:  50000
a: 100000

Use this to test your program: here

Output for test case:

j:  2
x:  3
q:  9
k: 32
v: 67
c: 73
w: 79
f: 93
b: 94
p: 96

Shortest code wins. You can submit a program or a function.

  • \$\begingroup\$ Do you want a function or a whole program? \$\endgroup\$ – marinus Oct 25 '12 at 4:14
  • \$\begingroup\$ @marinus: A program or a function, whichever is shorter for you. \$\endgroup\$ – beary605 Oct 25 '12 at 4:23
  • \$\begingroup\$ I presume from your output to the test case that uppercase and lowercase letters are treated as the same? \$\endgroup\$ – Gareth Oct 25 '12 at 10:31
  • \$\begingroup\$ @Gareth: Yes, they should be treated the same. \$\endgroup\$ – beary605 Oct 25 '12 at 14:17

13 Answers 13


K, 60

{(-:|/#:'f)$f:10#$(!x)[i]!r i:<r:. x:#:'=_x@&x in,/.Q`a`A`n}

Mathematica, 102 93:


              Last@# &],


         SortBy[Tally@StringCases[ToLowerCase@#, x_?LetterQ :> x <> ":"],
                #[[2]] &], 
         TableAlignments -> Right] &
  • \$\begingroup\$ You beat me to it (and had more streamlined code). \$\endgroup\$ – DavidC Oct 25 '12 at 18:58
  • \$\begingroup\$ @DavidCarraher But MMa is SO verbose! :( \$\endgroup\$ – Dr. belisarius Oct 25 '12 at 23:35
  • \$\begingroup\$ I detect a hint of irony in your comment. \$\endgroup\$ – DavidC Oct 26 '12 at 16:59

Bash, 72

Assuming no single-letter-named files in the current directory; input from stdin:

grep -io [a-z]|sort -f|uniq -ci|sort|head|sed -E 's/(.*)([a-z])/\2:\1/'

Works on Linux, but for some reason, my mac's grep seems to ignore the -i when used with -o (bug or undocumented gotcha?), but the following (78) works:

tr A-Z a-z|grep -o [a-z]|sort|uniq -c|sort|head|sed -E 's/(.*)([a-z])/\2:\1/'

If the output were allowed to be reversed, the sed part could be dropped to save 30 characters in each of the previous solutions.


Java 452 444 433 430

import java.util.*;
interface L{
static void main(String[]a){
Map<Character,Integer>m=new HashMap<>();
for(char c:a[0].toLowerCase().toCharArray())if(96<c&&c<123) m.merge(c,1,(j,i)->j+i);
int l=String.valueOf(m.values().stream().reduce(0,Integer::max)).length();

My first contribution. I know Java will never win this kind of challenges but until now it is at least the shortest java response :)

First argument is used as input. If it contains spaces, it has to be quoted(")


// there is not a lot you get for free, so import you will
import java.util.*;
interface L{
static void main(String[]a) {                 // java main entry point
    Map<Character,Integer> m=new HashMap<>(); // key=character, value=number of occurances in input text
    for(char c:                               // for each character in
        a[0]                                  // take first argument from command line
        .toLowerCase()                        // convert all characters to lowercase
        .toCharArray())                       // convert to array so we can loop over it
      if(96<c&&c<123)                      // accept only a-z
        m.merge(c, 1, (j,i)->j+i);            // fill map, increase corresponding value
    int l=String.valueOf(                     // l: to store length of counter
            m.values().stream()               // loop over counter values
              .reduce(0,Integer::max))        // find max value
            .length();                        // count number of digits of max value

    m.entrySet().stream()                     // loop over the counter map
    .sorted((e,f)->e.getValue()-f.getValue()) // sort the map by value ascending
    .forEachOrdered(                          // loop over the ordered result  
        e->System.out.println(                // print for every entry
            e.getKey()                        // - the key
            +String.format("%"+l+"s",         // - format using l
                 e.getValue())));             //   the value

452 -> 444 : removed spaces

444 -> 433 : class-> interface, public

433 -> 430: 96<c&c<123 (thanks @ceilingcat)

  • \$\begingroup\$ Nice first post, welcome to the site! I don't know Java, but is there a way some of that indentation could be removed to save some bytes? \$\endgroup\$ – Redwolf Programs Oct 29 '20 at 16:07
  • \$\begingroup\$ I'm senior developer and it kind a hurts to write unreadable code :). But as this challenge is more about the number of bytes, I removed unnecessary spaces. Thanks \$\endgroup\$ – Conffusion Oct 29 '20 at 16:35
  • 2
    \$\begingroup\$ We also have a Tips for golfing in Java page if you're interested in reading (or contributing!) some. \$\endgroup\$ – Redwolf Programs Oct 29 '20 at 16:39

APL (Dyalog Unicode), 45 36 bytes


Try it online!

A full program which accepts a string as input. tio link uses polyfills for and ⎕C.

Shortened and tested with help from Bubbler.

-12 bytes from Adám.

Doesn't work on tio due to some problem with ⎕C.(Now works, thanks to Adám's help)


                             ⍥⎕C      lowercase the following:
                                ⎕D,⎕A string of the alphabet and digits
                       ⎕              and the input
                        (∊⊆⊣)         filter out nonalphanumerics
                      ∊               flatten the string
            {⍺':',≢⍵}⌸                create a frequency table for each letter key
   ⌷⍨∘⊂∘⍋∘⌽⍨                          sort the table based on the last row
10↑                                   take the first 10 elements
  • \$\begingroup\$ Gofled a bit: {10↑⌷⍨∘⊂∘⍋∘⌽⍨{⍺':',≢⍵}⌸∊⍵(∊⊆⊣)⍥⎕C⎕D,⎕A} which you can make run on TIO by replacing and ⎕C with polyfills: Ö←{(⍵⍵ ⍺)⍺⍺(⍵⍵ ⍵} ⋄ ∆C←819⌶. Also, since this only uses the argument once, you can save another 2 bytes by converting to a tradfn. \$\endgroup\$ – Adám Oct 29 '20 at 8:10
  • \$\begingroup\$ @Adám Another interesting use for the ∊⊆⊣ idiom. I'll get it running. \$\endgroup\$ – Razetime Oct 29 '20 at 8:23
  • 1
    \$\begingroup\$ Listed here. \$\endgroup\$ – Adám Oct 29 '20 at 8:24

Python, 206 199 195 characters

import sys
for c in t:
 if c.isalnum():f[c]=f.get(c,0)+1
s=lambda x:x[1]
for n in h:print n[0]+': '+`n[1]`.rjust(len(`max(h,s)[1]`))

Undoubtedly there is a lot of room for improvement, but here's my first attempt. Produces identical output for the test case provided.

206 -> 199: Removed unnecessary newline and keyword argument

199 -> 195: iteritems()


Javascript, 271 characters

I'm not sure whether this breaks the rules of not (it doesn't justify the numbers in the output, but here's my solution anyway:

c=[];t=128;while(t--)c[t]=[0,t];i=prompt().toLowerCase();for(n=0;n<i.length;x=i.charCodeAt(n++),c[x][0]+=x>96&&x<123);for(n=i=0,c=c.sort(function(a,b){return(a[0]-b[0])});n<10;i++)if(c[i][0]>0){console.log(String.fromCharCode(c[i][1])+": "+("  "+c[i][0]).slice(-2));n++}

I'm pretty sure it can be simplified (I won't win with this character count :P), but now I can't find anything.

Okay, so I completely forgot that the padding was actually a requirement :O Corrected that and fixed a bug. (250 -> 271)


Haskell (229)

import Char
import Data.List
k x=[(l$z(==m)x,m)|m<-x]
s z=[b:':':take(1+(maximum$map(l.t.fst)z)-l(t a))(repeat ' ')++t a++"\n"|(a,b)<-z]
main=interact$concat.s.take 10.sort.nub.k.map toLower.z isAlphaNum

Perl, 86 chars

given string is in $_


Python, 144

import os
for c in a:print c+': %%%ii'%len(`n(a[-1])`)%n(c)
  • \$\begingroup\$ I just proposed an edit that saves you 14 characters by switching to the 'raw_input()' function. \$\endgroup\$ – ESultanik Oct 25 '12 at 14:25
  • \$\begingroup\$ @ESultanik The problem is that raw_input() terminates on newlines, so it would stop after the first line of input in the supplied test case. \$\endgroup\$ – Strigoides Oct 25 '12 at 15:38
  • \$\begingroup\$ @Strigoides: Good point; I missed that the input could have newlines. My fault for not looking at the test cases. \$\endgroup\$ – ESultanik Oct 25 '12 at 15:48
  • \$\begingroup\$ print c+': %%%ii'%len(n(a[-1]))%n(c) saves 3 characters \$\endgroup\$ – Daniel Oct 27 '12 at 9:47
  • \$\begingroup\$ @Daniel: Very nice; thanks. \$\endgroup\$ – grc Oct 27 '12 at 11:34

JavaScript 1.8 (ES6) 186

  s.slice(0,10).map(x=>x+': '+('      '+n[x]).slice(-(n[s[9]]+'').length))

Jelly, 22 bytes


Try it online!

Doesn't output with a : on each row, but given that isn't explicitly specified in the spec (compared to the left padding), I assumed this was OK. 26 bytes to require separating by :.

How it works

Øa;ØD - Helper link. Yield a constant value "abc...xyz012...789"
Øa    - Yield "abc...xyz"
   ØD - Yield "0123456789"
  ;   - Concatenate

ŒlċⱮ¢¢żPƇ0ị$Þḣ⁵G - Main link. Takes a string S on the left
Œl               - Convert S to lowercase
    ¢            - Yield "abc...789"
   Ɱ             - For each character:
  ċ              -   Count occurrences in S
     ¢           - Yield "abc...789"
      ż          - Zip with occurrences
        Ƈ        - Keep characters where:
       P         -   There is at least 1 occurrence in S
           $Þ    - Sort by:
         0ị      -   Number of occurrences
             ḣ⁵  - Take the first 10
               G - Convert into a grid format
  • \$\begingroup\$ since all the other answers do output with a : character, I think it's a better idea to post the 26 byte version. \$\endgroup\$ – Razetime Oct 29 '20 at 6:52

Japt v2.0a0 -R, 28 bytes

Yet another old challenge hindered by an unnecessarily cumbersome output format.

r\W v ü ñʮάpZÊïA yù ®q": 

Try it (Note: I'm passing the input in the header here so it can be enclosed in backticks, allowing for the inclusion of both single & double quotes. Doing so, however, makes no difference to the code or byte count.)

r\W v ü ñʮάpZÊïA yù ®q":      :Implicit input of string
r                                :Remove
 \W                              :  /[^A-Z0-9]/gi
    v                            :Lowercase
      ü                          :Group & sort
        ñ                        :Sort by
         Ê                       :  Length
          ®                      :Map each Z
           Î                     :  First character
            ¬                    :  Split
             p                   :  Push
              ZÊ                 :    Length of Z
                Ã                :End map
                 ¯               :Slice to
                  A              :  10th element
                    y            :Transpose
                     ù           :Left pad each sub array with spaces to the length of the longest and implicitly transpose back
                       ®         :Map
                        q":      :  Join with ": "
                                 :Implicit output joined with newlines
  • \$\begingroup\$ link says Error: URI too long \$\endgroup\$ – Razetime Oct 29 '20 at 6:51
  • \$\begingroup\$ Fixed that earlier, @Razetime - thanks. \$\endgroup\$ – Shaggy Oct 29 '20 at 16:21

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.