28
\$\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\$
11
  • 5
    \$\begingroup\$ Does case matter (ie is This the same as this and tHIs)? \$\endgroup\$
    – Gareth
    Commented Jan 29, 2014 at 7:49
  • \$\begingroup\$ If anything non-alphanumeric counts as a separator, is wouldn't 2 words (wouldn and t)? \$\endgroup\$
    – Gareth
    Commented Jan 29, 2014 at 7:51
  • \$\begingroup\$ @Gareth Should be case sensitive, This and this would be indeed two different words, same wouldnand t. \$\endgroup\$ Commented Jan 29, 2014 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
    Commented Jan 29, 2014 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\$ Commented Jan 29, 2014 at 9:18

46 Answers 46

1
2
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\$
1
  • \$\begingroup\$ Then it's a problem of the ECMA one :) \$\endgroup\$
    – Ven
    Commented Mar 25, 2014 at 10:11
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\$
2
  • \$\begingroup\$ @m.buettner Thanks for the tip on the epxression! :) \$\endgroup\$
    – Abbas
    Commented Jun 30, 2014 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\$ Commented Jun 30, 2014 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\$
8
  • \$\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\$ Commented Jan 29, 2014 at 22:23
  • \$\begingroup\$ Thanks. I will edit it. How can we do it without manually deleting spaces? \$\endgroup\$ Commented Jan 29, 2014 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\$ Commented Jan 29, 2014 at 22:27
  • \$\begingroup\$ What's wrong with manually deleting spaces? \$\endgroup\$ Commented Jan 29, 2014 at 22:28
  • 1
    \$\begingroup\$ That said, yours doesn't handle the : in the input, which should be treated as a separator \$\endgroup\$ Commented Jan 29, 2014 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\$
1
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\$
1
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\$
2
  • \$\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
    Commented Jan 30, 2014 at 9:21
  • \$\begingroup\$ -1. : should not be part of a word \$\endgroup\$ Commented Mar 24, 2014 at 22:35
1
2

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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