26
\$\begingroup\$

The code should take input a text (not mandatory can be anything file, stdin, string for JavaScript, etc):

This is a text and a number: 31.

The output should contain the words with their number of occurrence, sorted by the number of occurrences in descending order:

a:2
and:1
is:1
number:1
This:1
text:1
31:1

Notice that 31 is a word, so a word is anything alpha-numeric, number are not acting as separators so for example 0xAF qualifies as a word. Separators will be anything that is not alpha-numeric including .(dot) and -(hyphen) thus i.e. or pick-me-up would result in 2 respectively 3 words. Should be case sensitive, This and this would be two different words, ' would also be separator so wouldnand t will be 2 different words from wouldn't.

Write the shortest code in your language of choice.

Shortest correct answer so far:

\$\endgroup\$
  • 5
    \$\begingroup\$ Does case matter (ie is This the same as this and tHIs)? \$\endgroup\$ – Gareth Jan 29 '14 at 7:49
  • \$\begingroup\$ If anything non-alphanumeric counts as a separator, is wouldn't 2 words (wouldn and t)? \$\endgroup\$ – Gareth Jan 29 '14 at 7:51
  • \$\begingroup\$ @Gareth Should be case sensitive, This and this would be indeed two different words, same wouldnand t. \$\endgroup\$ – Eduard Florinescu Jan 29 '14 at 9:08
  • \$\begingroup\$ If Wouldn't are 2 words, shouldn't it be "Would" and "nt" since its short for Would not, or is that to much grammer nazi-ish? \$\endgroup\$ – Teun Pronk Jan 29 '14 at 9:12
  • \$\begingroup\$ @TeunPronk I try to keep it simple, putting a few rules will encourage exceptions to be in order with grammar , and there are a lot of exceptions out there.Ex in English i.e. is a word but if we let the dot all the dots at the end of phrases will be taken, same with quotes or single quotes, etc. \$\endgroup\$ – Eduard Florinescu Jan 29 '14 at 9:18

47 Answers 47

1
\$\begingroup\$

J, 35+?

(~.(([\:]);]\:]),.@(+/"1@=))@(>@;:)

Doesn't fully work though. Problem is that the splitting into words ';:' monad doesn't handle non-alplanumeric characters in quite the right way. Any suggestions?

Here's how you use it:

(~.(([\:]);]\:]),.@(+/"1@=))@(>@;:) 'This is a text and a number: 31.'
┌───────┬─┐
│a      │2│
│This   │1│
│is     │1│
│text   │1│
│and    │1│
│number:│1│
│31.    │1│
└───────┴─┘ 
\$\endgroup\$
1
\$\begingroup\$

Clojure

(defn wc [s]
  (let [mc #(assoc % %2 (inc (get % %2 0)))]
    (sort-by #(- (val %))
             (reduce mc {} (re-seq #"\w+" (.toLowerCase s))))))

example:

(wc "hi mom hi dad hello peter hello dad hi")
;; (["hi" 3] ["hello" 2] ["dad" 2] ["peter" 1] ["mom" 1])
\$\endgroup\$
1
\$\begingroup\$

Python (95):

a=sorted(raw_input().split(__import__("string").punctuation))
for i in set(a):print i,a.count(i)

Pretty straightforward, I'd say.

\$\endgroup\$
1
\$\begingroup\$

LiveScript - 74 (translation of ECMA one)

s.match(/[^_\W]+/g,a={})map (->-~=a[it]),keys(a)map(->[it,a[it]])sort (.1-&1.1)
\$\endgroup\$
  • \$\begingroup\$ Then it's a problem of the ECMA one :) \$\endgroup\$ – Ven Mar 25 '14 at 10:11
  • \$\begingroup\$ Did so -- thanks. \$\endgroup\$ – Ven Mar 25 '14 at 12:58
1
\$\begingroup\$

Mathematica, 56 bytes

What, no Mathematica answer yet? This one is not quite a winner, but it's both concise and quite expressive:

f=SortBy[Tally@StringCases[#,WordCharacter..],-Last@#&]&

Calling f["This is a text and a number: 31."] yields

{
 {"a", 2},
 {"31", 1},
 {"and", 1},
 {"is", 1},
 {"number", 1},
 {"text", 1},
 {"This", 1}
}
\$\endgroup\$
1
\$\begingroup\$

shell utils, also 42 characters

tr -sc [:alnum:] \\n|sort|uniq -c|sort -rn

Another approach that equals Thor's answer.

\$\endgroup\$
1
\$\begingroup\$

Smalltalk, 91 78

input in s

(s allRegexMatches:'\w+')asBag valuesAndCountsDo:[:c :n|(c,$:)print.n printNL]

input:

pick-me-up This is a text and a number: 31.

output:

31:1
pick:1
text:1
me:1
number:1
up:1
is:1
a:2
and:1
This:1
\$\endgroup\$
1
\$\begingroup\$

Will never win but wanted to try anyway...

C# (118)

var i = "This is a text and a number: 31.";
//The above line is not counted
Regex.Split(i,@"[\W_]").Where(w=>w!="").GroupBy(g=>g).OrderBy(o=>-o.Count()).Select(s=>new{s.Key,V=s.Count()}).Dump();

Ungolfed:

Regex.Split(i, @"[\W_]")                         //split by special chars
     .Where(w => w != "")                        //remove empty
     .GroupBy(g => g)                            //group by word
     .OrderBy(o => -o.Count())                   //order by reversed count
     .Select( s => new { s.Key, V = s.Count() }) //select value and count
     .Dump();                                    //write to screen (LinQPad)
\$\endgroup\$
  • \$\begingroup\$ @m.buettner Thanks for the tip on the epxression! :) \$\endgroup\$ – Abbas Jun 30 '14 at 14:16
  • \$\begingroup\$ Actually, \W|_ is another byte shorter. I was wondering if you could save the Where call if you used a slightly more advanced expression, but I can't seem to find a way to deal with a leading or trailing "", if the string doesn't start with an alphanumeric character. \$\endgroup\$ – Martin Ender Jun 30 '14 at 14:21
1
\$\begingroup\$

J 69

Should handle corner-cases like ' - etc, at the huge cost of including all alphanumeric characters (u:62$,65 97 48+/i.26):

(>@~.,.':',.":@#/.~)(#~*@#&>)(<;._1~-.@e.&(u:62$,65 97 48+/i.26))'.',

Usage: just append whatever string to be counted bewteen single quotes (mind that you need to double single quotes in the string).

Example:

   (>@~.,.':',.":@#/.~)(#~*@#&>)(<;._1~-.@e.&(u:62$,65 97 48+/i.26))'.','This is a text and a number: 31. More-tests wouldn''t be bad'
This  :1
is    :1
a     :2
text  :1
and   :1
number:1
31    :1
More  :1
tests :1
wouldn:1
t     :1
be    :1
bad   :1
\$\endgroup\$
1
\$\begingroup\$

Python 2 - 61

Assuming the input is in variable s, which is more realistic in real programming than user input anyway.

import re,collections as c
print c.Counter(re.split('\W+',s))

output

Counter({'a': 2, 'and': 1, '': 1, 'This': 1, 'text': 1, 'is': 1, 'number': 1, '31': 1})

This isn't really good output me thinks. It has an empty word and isn't readable. Here's a version with neat output (90):

Python 2 (neat) - 90

import re,collections as c
d=c.Counter(re.split('\W+',s))
for w in d:
  if w:print w+':'+`d[w]`

output

a:2
and:1
This:1
text:1
is:1
number:1
31:1
\$\endgroup\$
1
\$\begingroup\$

Clojure

(defn count-words [string]
  (as-> string s
        (clojure.string/replace s #"[^a-zA-Z0-9 ]" "")
        (clojure.string/split s #"\W")
        (frequencies s)
        (sort-by val s)
        (reverse s)))

(clojure.pprint/pprint (count-words "This is a text and a number: 31."))

;; => (["a" 2] ["31" 1] ["number" 1] ["and" 1] ["text" 1] ["is" 1] ["This" 1])

I thought I would beat markw's concision with this approach, but I didn't.

\$\endgroup\$
1
\$\begingroup\$

Perl 6, 53 39 bytes

{.comb(/<-[_\W]>+/).Bag.sort:{-.value}}

I use .comb to find every word matching the regexp <-[_\W]>+. In Perl 6, character classes are written <[]> instead of [], and negative character classes <-[]> instead of [^].

We then transform the list of words to a Bag (a set that keeps the number of occurences), and we sort said bag by their value.

\$\endgroup\$
0
\$\begingroup\$

C# (332)

This is my original program after excluding white spaces. Please pardon me if I did mistake in counting

using System;
using System.Collections.Generic;
using System.Linq;

class P
{        
    static void Main(String[] A)
    {           
        Dictionary<string,int> D=new Dictionary<string,int>();
        foreach(string s in A)
        {
            if(!D.ContainsKey(s))
                D.Add(s,1);
            else D[s]+= 1;
        }
        foreach(KeyValuePair<string,int> i in D.OrderByDescending(k=>k.Value))
            Console.WriteLine(i.Key+":" + i.Value);
    }
}

This is program with lots of character saving after suggestions from my dear friend jimbobmcgee

using System;
using System.Collections.Generic;
using System.Linq;

class P
{
    static void Main(string[] A)
    {        
       var D = new Dictionary<string, int>();
       foreach (var v in A)
       {
          if (!D.ContainsKey(v))
            D[v] = 1;
          else D[v] += 1;
       }
     foreach (var v in D.OrderBy(k=>-k.Value))
     {
        Console.WriteLine(v.Key+": "+v.Value);
     }
   }
}

Output

enter image description here

\$\endgroup\$
  • \$\begingroup\$ Assuming you are ripping out all whitespace, I make it 332: using System;using System.Collections.Generic;using System.Linq;class P{static void Main(String[]A){Dictionary<string,int>D=new Dictionary<string,int>();foreach(string s in A){if(!D.ContainsKey(s))D.Add(s,1);else D[s]+=1;}foreach(KeyValuePair<string,int> i in D.OrderByDescending(k=>k.Value))Console.WriteLine(i.Key+":"+i.Value);}} \$\endgroup\$ – jimbobmcgee Jan 29 '14 at 22:23
  • \$\begingroup\$ Thanks. I will edit it. How can we do it without manually deleting spaces? \$\endgroup\$ – Merin Nakarmi Jan 29 '14 at 22:25
  • 1
    \$\begingroup\$ remove 19c if Dictionary<string,int> D=... becomes var D=...; same with foreach(string s... becoming (foreach(var s...` for 3c; D.Add(s,1); can become D[s]=1 for a 5c saving; same with foreach(KeyValuePair... becoming foreach(var... for 21c... \$\endgroup\$ – jimbobmcgee Jan 29 '14 at 22:27
  • \$\begingroup\$ What's wrong with manually deleting spaces? \$\endgroup\$ – jimbobmcgee Jan 29 '14 at 22:28
  • 1
    \$\begingroup\$ That said, yours doesn't handle the : in the input, which should be treated as a separator \$\endgroup\$ – jimbobmcgee Jan 29 '14 at 22:37
0
\$\begingroup\$

JavaScript (ES5), 146

 F=Function;a={};r="return ";b="b";x=prompt().match(/\w+/g);alert(""+x.filter(F(b,r+"!~(a[b]=~-a[b])")).map(F(b,r+"-a[b]+':'+b")).sort().reverse())

=>| This is a text and a number: 31.
<=| 2:a,1:text,1:number,1:is,1:and,1:This,1:31

\$\endgroup\$
0
\$\begingroup\$

Python3 (244 bytes)

import re, collections as c, sys
t=''
with open(sys.argv[1]) as f:
    t+=f.read()
d=c.Counter([s for s in (re.sub('[\s\W]','\n',t).split('\n')) if s])
for x in sorted(d.items(), key=lambda x: x[1])[::-1]:
    print('{}:{}'.format(x[0],x[1]))

Works also with newlines, tabs etc. in the sample text.

Output

> $ python3 wc.py test.txt
a:2
text:1
is:1
This:1
and:1
number:1
31:1
\$\endgroup\$
0
\$\begingroup\$

SQL (Oracle 11+)

with stbl as (
select level,instr('This is a text and a number: 31.',' ',1,level) ins
        ,decode(level,1,substr('This is a text and a number: 31.',0,instr('This is a text and a number: 31.',' ',1,level)-1),
             substr('This is a text and a number: 31.',instr('This is a text and a number: 31.',' ',1,level-1)+1,
                                                   (instr('This is a text and a number: 31.',' ',1,level)  - instr('This is a text and a number: 31.',' ',1,level-1))       
                                                   ) )word
from dual
connect by level <= regexp_count('This is a text and a number: 31.', ' ')
union all
select null,null,substr('This is a text and a number: 31.', instr('This is a text and a number: 31.', ' ',-1)+1) from dual
)
select word,count(*) 
from stbl
group by word
order by 1
;

Output:

WORD                               COUNT(*)
-------------------------------- ----------
31.                                       1 
This                                      1 
a                                         2 
and                                       1 
is                                        1 
number:                                   1 
text                                      1 

 7 rows selected 
\$\endgroup\$
0
\$\begingroup\$

JAVA CODE

 String s="This is a text and a number: 31";
                String[] stringArray = s.split(" ");
                final Map<String, Integer> counter = new HashMap<String, Integer>();
                for (String str : stringArray)
                    counter.put(str, 1 + (counter.containsKey(str)counter.get(str): 0));
                List<String> list = new ArrayList<String>(counter.keySet());
                Collections.sort(list, new Comparator<String>() {
                    @Override
                    public int compare(String x, String y) {
                        return counter.get(y) - counter.get(x);
                    }
                });
                list.toArray(new String[list.size()]);
                for (String str : list) {
                    int frequency = counter.get(str);
                    System.out.println(str + ":" + frequency);
                }

OUTPUT

 a:2
 text:1
 is:1
 31:1
 number::1
 This:1
 and:1
\$\endgroup\$
  • \$\begingroup\$ This is a code-golf question. At the very least you could remove all unnecessary spaces (like between ) and {) and shorten all variable names to 1 character. \$\endgroup\$ – Gareth Jan 30 '14 at 9:21
  • \$\begingroup\$ -1. : should not be part of a word \$\endgroup\$ – n̴̖̋h̷͉̃a̷̭̿h̸̡̅ẗ̵̨́d̷̰̀ĥ̷̳ Mar 24 '14 at 22:35

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.