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
    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
    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\$ 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
    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\$ Jan 29, 2014 at 9:18

46 Answers 46

27
\$\begingroup\$

grep and coreutils  44  42

grep -io '[a-z0-9]*'|sort|uniq -c|sort -nr

Test:

printf "This is a text and a number: 31." |
grep -io '[a-z0-9]*'|sort|uniq -c|sort -nr

Results in:

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

Update

  • Use case-insensitive option and shorter regex. Thanks Tomas.
\$\endgroup\$
8
  • 2
    \$\begingroup\$ This being almost exactly McEllroy's response to Knuth's book Literate Programming. The only difference being that this does not include a pipe into head at the end. \$\endgroup\$ Jan 29, 2014 at 10:54
  • \$\begingroup\$ This was pretty much my first thought too. \$\endgroup\$
    – Rob
    Jan 29, 2014 at 17:15
  • 1
    \$\begingroup\$ Wouldn't '\w+' work as well? \$\endgroup\$
    – Sylwester
    Jan 30, 2014 at 18:05
  • 1
    \$\begingroup\$ 41 characters: grep -io \[A-Z0-9]*|sort|uniq -c|sort -nr \$\endgroup\$
    – Tomas
    Feb 1, 2014 at 16:16
  • 1
    \$\begingroup\$ @Tomas: Added this to the answer, thanks. I left in protection for the asterisk, because it was expanding file names in some shells. \$\endgroup\$
    – Thor
    Feb 26, 2014 at 10:10
18
\$\begingroup\$

Java 8: 289

Which is pretty good, since java is a very non-golfy language.

import java.util.stream.*;class C{static void main(String[]a){Stream.of(a).flatMap(s->of(s.split("[\\W_]+"))).collect(Collectors.groupingBy(x->x,Collectors.counting())).entrySet().stream().sorted(x,y->x.getValue()-y.getValue()).forEach(e->System.out.println(e.getKey()+":"+e.getValue()));}

Ungolfed:

import java.util.stream.*;
class C {
    static void main(String [] args){
        Stream.of(args).flatMap(arg->Stream.of(arg.split("[\\W_]+")))
            .collect(Collectors.groupingBy(word->word,Collectors.counting()))
            .entrySet().stream().sorted(x,y->x.getValue()-y.getValue())
            .forEach(entry->System.out.println(entry.getKey()+":"+entry.getValue()));
    }
}

Run from the command line:

java -jar wordCounter.jar This is a text and a number: 31.
\$\endgroup\$
4
  • \$\begingroup\$ Wrong regex for splitting. It should be "[^\\W_]" \$\endgroup\$ Mar 24, 2014 at 22:40
  • \$\begingroup\$ @n̴̖̋h̷͉̃a̷̭̿h̸̡̅ẗ̵̨́d̷̰̀ĥ̷̳, the String.split(String regex) method takes a pattern that matches the delimiter to split on. So, for instance, "aababba".split("b") would yield the array {"aa", "a", "", "a"}. My regex [^\\w\\d] means 'a character in neither the word character nor digit character classes'. [^\\W_] is instead 'a character that is neither an underscore nor is in the non-word-character class' and would match any word character except the underscore. \$\endgroup\$ Mar 25, 2014 at 16:10
  • \$\begingroup\$ Sorry, my previous comment was incorrect. \w includes \d, so \d is redundant. \w includes underscore, which should be considered a separator according to the question. So the correct regex for splitting should be "[\\W_]+". \$\endgroup\$ Mar 25, 2014 at 16:30
  • \$\begingroup\$ @n̴̖̋h̷͉̃a̷̭̿h̸̡̅ẗ̵̨́d̷̰̀ĥ̷̳ ok, thanks; I've fixed the problem. \$\endgroup\$ Mar 25, 2014 at 18:47
17
\$\begingroup\$

APL (57)

⎕ML←3⋄G[⍒,1↓⍉G←⊃∪↓Z,⍪+⌿∘.≡⍨Z←I⊂⍨(I←⍞)∊⎕D,⎕A,⎕UCS 96+⍳26;]

e.g.

      ⎕ML←3⋄G[⍒,1↓⍉G←⊃∪↓Z,⍪+⌿∘.≡⍨Z←I⊂⍨(I←⍞)∊⎕D,⎕A,⎕UCS 96+⍳26;]
This is a text and a number: 31.
 a       2
 This    1
 is      1
 text    1
 and     1
 number  1
 31      1

Explanation:

  • ⎕D,⎕A,⎕UCS 96+⍳26: numbers, uppercase letters, lowercase letters
  • (I←⍞)∊: read input, store in I, see which ones are alphanumeric
  • Z←I⊂⍨: split I in groups of alphanumeric characters, store in Z
  • +⌿∘.≡⍨Z: for each element in Z, see how often it occurs
  • Z,⍪: match each element in Z pairwise with how many times it occurs
  • G←⊃∪↓: select only the unique pairs, store in G
  • ⍒,1↓⍉G: get sorted indices for the occurrences
  • G[...;]: reorder the lines of G by the given indices
\$\endgroup\$
5
  • 6
    \$\begingroup\$ what... the... f..... \$\endgroup\$
    – Ozh
    Jan 30, 2014 at 10:49
  • 6
    \$\begingroup\$ This is why I have nightmares. \$\endgroup\$
    – BlueBuddy
    Jan 30, 2014 at 14:50
  • 3
    \$\begingroup\$ @Thebluefish: APL was designed from a notation, with the intention that much like maths, a concise notation frees you to think clearly. Again like maths, when you first see that notation, you tend to think it's not clear at all, but languages always seem complex to begin with. It would be easier if it wasn't all on one line, though... \$\endgroup\$
    – Phil H
    Feb 3, 2014 at 15:01
  • \$\begingroup\$ whatever you come up with in APL I only see some unicode garbage, arrows pointing in directions and an upside down pine tree. that is worse than J \$\endgroup\$
    – bebe
    Jun 30, 2014 at 18:18
  • \$\begingroup\$ Could be shorter with ⎕s (help.dyalog.com/latest/Content/Language/System%20Functions/…) and the new key operator (help.dyalog.com/latest/Content/Language/Primitive%20Operators/…): g⌷⍨⊂⍒2⌷⍉g←{⍺,≢⍵}⌸('\w+'⎕s'\0')⍞ \$\endgroup\$
    – ngn
    Jul 1, 2014 at 9:23
8
\$\begingroup\$

C#: 153c 144c 142c 111c 115c 118c 114c 113c

(via LINQPad in "C# Statements" mode, not including input string)

Version 1: 142c

var s = "This is a text and a number: 31."; // <- line not included in count
s.Split(s.Where(c=>!Char.IsLetterOrDigit(c)).ToArray(),(StringSplitOptions)1).GroupBy(x=>x,(k,e)=>new{s,c=e.Count()}).OrderBy(x=>-x.c).Dump();

Ungolfed:

var s = "This is a text and a number: 31.";
s.Split(                                                     // split string on multiple separators
    s.Where(c => !Char.IsLetterOrDigit(c))                   // get list of non-alphanumeric characters in string
     .ToArray(),                                             // (would love to get rid of this but needed to match the correct Split signature)
    (StringSplitOptions)1                                    // integer equivalent of StringSplitOptions.RemoveEmptyEntries
).GroupBy(x => x, (k, e) => new{ s = k, c = e.Count() })     // count by word
 .OrderBy(x => -x.c)                                         // order ascending by negative count (i.e. OrderByDescending)
 .Dump();                                                    // output to LINQPad results panel

Results:

Results

Version 2: 114c

([\w] includes _, which is incorrect!; [A-z] includes [ \ ] ^ _ `; settling on [^_\W]+)

var s = "This is a text and a number: 31."; // <- line not included in count
Regex.Matches(s, @"[^_\W]+").Cast<Match>().GroupBy(m=>m.Value,(m,e)=>new{m,c=e.Count()}).OrderBy(g=>-g.c).Dump();

Ungolfed:

Regex.Matches(s, @"[^_\W]+")                                   // get all matches for one-or-more alphanumeric characters
     .Cast<Match>()                                            // why weren't .NET 1 collections retrofitted with IEnumerable<T>??
     .GroupBy(m => m.Value, (m,e) => new{ m, c = e.Count() })  // count by word
     .OrderBy(g => -g.c)                                       // order ascending by negative count (i.e. OrderByDescending)
     .Dump();                                                  // output to LINQPad results panel

Results: (as Version 1)

\$\endgroup\$
2
  • \$\begingroup\$ By the way, for version 2, your ungolfed version does not match your golfed version. And since you are using literal string, you can write @"[^_\W]" \$\endgroup\$ Mar 25, 2014 at 10:14
  • \$\begingroup\$ @n̴̖̋h̷͉̃a̷̭̿h̸̡̅ẗ̵̨́d̷̰̀ĥ̷̳ - fixed the typo and removed the extra `` for a 1-char saving -- thanks!! \$\endgroup\$ Mar 26, 2014 at 19:40
7
\$\begingroup\$

R, 58 char

sort(table(unlist(strsplit(scan(,""),"[[:punct:]]"))),d=T)

Usage:

sort(table(unlist(strsplit(scan(,""),"[[:punct:]]"))),d=T)
1: This is a text and a number: 31.
9: 
Read 8 items

     a     31    and     is number   text   This 
     2      1      1      1      1      1      1 
\$\endgroup\$
1
  • \$\begingroup\$ This is shorter (49 chars) sort(table(gsub("[[:punct:]]","",scan(,""))),d=T). Unfortunately both solutions doesn't work correctly for wouldn't. \$\endgroup\$
    – djhurio
    Jul 1, 2014 at 10:14
6
\$\begingroup\$

perl6: 49 characters

.say for get.comb(/\w+/).Bag.pairs.sort(-*.value)

Comb input for stuff matching \w+, put resulting list of words in a Bag, ask for their pairs and sort them by negative value. (The * is a Whatever star, it's not multiplication here)

output:

"a" => 2
"This" => 1
"is" => 1
"text" => 1
"and" => 1
"number" => 1
"31" => 1
\$\endgroup\$
5
  • 3
    \$\begingroup\$ Perl 6 scares me. \$\endgroup\$
    – primo
    Jan 29, 2014 at 19:24
  • 1
    \$\begingroup\$ Every time I think of a cool language feature, I look for it and it's in Perl6 somewhere. That's why it's taking a long time... \$\endgroup\$
    – Phil H
    Jan 30, 2014 at 13:49
  • \$\begingroup\$ You can trim 6 characters by using .words instead of .comb(/\w+/) :) \$\endgroup\$
    – Mouq
    Feb 15, 2014 at 23:42
  • \$\begingroup\$ @Mouq: unfortunately .words doesn't strip the : or . from the input as required :( \$\endgroup\$
    – Ayiko
    Feb 15, 2014 at 23:59
  • \$\begingroup\$ -1. _ should not be included in a word under the problem statement. \$\endgroup\$ Mar 24, 2014 at 22:24
6
\$\begingroup\$

Python 101 97

import re
a=re.split('[_\W]+',input())
f=a.count
for w in sorted(set(a),key=f)[::-1]:print w,f(w)

Now works with newline:

$ python countword.py <<< '"This is    a text and a number: 31, and a\nnewline"'
a 3
and 2
31 1
number 1
newline 1
is 1
text 1
This 1
\$\endgroup\$
2
  • \$\begingroup\$ This doesn't work when there are newlines or more than one consecutive space in text. \$\endgroup\$
    – klingt.net
    Jan 30, 2014 at 12:17
  • \$\begingroup\$ @klingt.net fixed. \$\endgroup\$
    – daniero
    Jan 30, 2014 at 13:03
6
\$\begingroup\$

PHP - 84 bytes

<?$a=array_count_values(preg_split('/[_\W]+/',$argv[1],0,1));arsort($a);print_r($a);

Input is accepted as a command line argument, e.g.:

$ php count-words.php "This is a text and a number: 31."

Output for the sample string:

Array
(
    [a] => 2
    [number] => 1
    [31] => 1
    [and] => 1
    [text] => 1
    [is] => 1
    [This] => 1
)
\$\endgroup\$
4
  • 1
    \$\begingroup\$ it says input is what you want. so you can get it as command line parameter using $argv[1] \$\endgroup\$
    – Einacio
    Jan 29, 2014 at 19:13
  • \$\begingroup\$ @Einacio good call. \$\endgroup\$
    – primo
    Jan 29, 2014 at 19:17
  • \$\begingroup\$ -1. Underscore _ should not be included in a word. \$\endgroup\$ Mar 24, 2014 at 22:25
  • \$\begingroup\$ @n̴̖̋h̷͉̃a̷̭̿h̸̡̅ẗ̵̨́d̷̰̀ĥ̷̳ fixed. \$\endgroup\$
    – primo
    Mar 29, 2014 at 9:07
5
\$\begingroup\$

PowerShell (40)

$s -split"\W+"|group -ca|sort count -des

$s is a variable that contains the input string.

\$\endgroup\$
5
  • 2
    \$\begingroup\$ [\W] isn't good enough - it is matching a space in my test. And it is not ordered by descending count... \$\endgroup\$ Jan 29, 2014 at 21:09
  • \$\begingroup\$ $s -split"[\W]"|group -ca|where{$_.Name -ne ""}|sort{-$_.Count} gets you closer (with costs, of course) \$\endgroup\$ Jan 29, 2014 at 21:21
  • \$\begingroup\$ Ooops I missed the sorting part. Will fix my answer shortly. \$\endgroup\$
    – microbian
    Jan 29, 2014 at 21:32
  • \$\begingroup\$ alternatively: $s -split"\W+"|group -ca |sort count -des \$\endgroup\$
    – Nacimota
    Jan 29, 2014 at 23:00
  • 4
    \$\begingroup\$ -split"\W+" is matching an empty string between the last . and the end of the string; also \W+ matches _ which is technically not allowed \$\endgroup\$ Jan 30, 2014 at 1:07
5
\$\begingroup\$

GNU awk + coreutils: 71 69

gawk 'BEGIN{RS="\\W+"}{c[$0]++}END{for(w in c)print c[w],w}'|sort -nr

Although gawk asort works on associative arrays, it apparently does not preserve the index values, necessitating the external sort

printf "This is a text and a number: 31." | 
gawk 'BEGIN{RS="\\W+"}{c[$0]++}END{for(w in c)print c[w],w}'|sort -nr
2 a
1 This
1 text
1 number
1 is
1 and
1 31

GNU awk 4.x: 100 93

A slightly larger but pure gawk solution using PROCINFO to set the default sort order for the associative array (appears to require a relatively recent gawk - > 4.x?)

BEGIN{RS="\\W+";PROCINFO["sorted_in"]="@val_num_desc"}
{c[$0]++}
END{for(w in c)print c[w],w}
\$\endgroup\$
4
  • \$\begingroup\$ Oooooh. I didn't know about PROCINFO. As if I needed another excuse to use awk in my life. Curse you! \$\endgroup\$ Feb 1, 2014 at 7:50
  • \$\begingroup\$ @dmckee TBH I didn't know about PROCINFO until I started poking around - I was convinced there had to be a way to do the sort natively - just a pity the identifiers are so long ;) \$\endgroup\$ Feb 1, 2014 at 14:55
  • \$\begingroup\$ In the bad old days there simply wasn't a way. Which leads to things like this old answer of mine. \$\endgroup\$ Feb 1, 2014 at 17:00
  • \$\begingroup\$ -1. Underscore _ should not be included in a word. \$\endgroup\$ Mar 24, 2014 at 22:26
4
\$\begingroup\$

Perl 69

$h{$_}++for<>=~/\w+/g;print"$_: $h{$_}
"for sort{$h{$b}-$h{$a}}keys%h

Added recommendations from @primo and @protist

\$\endgroup\$
7
  • 1
    \$\begingroup\$ What about sorting? \$\endgroup\$
    – daniero
    Jan 29, 2014 at 11:52
  • \$\begingroup\$ @daniero, excellent point! This now sorts! \$\endgroup\$ Jan 29, 2014 at 11:56
  • 1
    \$\begingroup\$ I think that's about as terse as it can get. If you don't mind a deprecation warning, no space is required between ge and for. Also, the <=> operator can be replaced by -. \$\endgroup\$
    – primo
    Jan 29, 2014 at 13:43
  • 2
    \$\begingroup\$ @primo Ahhh - instead of <=> is genius, not sure that's on the golfing tips for Perl thread. I'll update this later, thanks! \$\endgroup\$ Jan 29, 2014 at 14:32
  • 1
    \$\begingroup\$ Hey @protist, \w includes numbers too (perl -e 'print for"a 1 2 3 4 b"=~/\w/g' prints a1234b), but your mechanism for iterating the words saves another character so I'll update. Thank you! \$\endgroup\$ Jan 31, 2014 at 8:49
4
\$\begingroup\$

Powershell: 57 55 53 62 57

(not including input string)

$s = "This is a text and a number: 31."    # <-- not counting this line...
[Regex]::Matches($s,"[^_\W]+")|group -ca|sort{-$_.Count}

returns:

Count Name                      Group
----- ----                      -----
    2 a                         {a, a}
    1 and                       {and}
    1 31                        {31}
    1 number                    {number}
    1 This                      {This}
    1 is                        {is}
    1 text                      {text}

(with props to @microbian for group -ca)

\$\endgroup\$
3
\$\begingroup\$

EcmaScript 6

Version 1 (108 characters)

s.split(_=/[^a-z\d]/i).map(x=>_[x]=-~_[x]);keys(_).sort((a,b)=>_[a]<_[b]).map(x=>x&&console.log(x+':'+_[x]))

Version 2 (102 characters)

s.split(_=/[^a-z\d]/i).map(x=>_[x]=-~_[x]);keys(_).sort((a,b)=>_[a]<_[b]).map(x=>x&&alert(x+':'+_[x]))

Version 3 (105 characters)

s.match(_=/\w+/g).map(x=>_[x]=-~_[x]);alert(keys(_).sort((a,b)=>_[a]<_[b]).map(x=>x+':'+_[x]).join('\n'))

Version 4 (94 characters)

s.match(_=/\w+/g).map(x=>_[x]=-~_[x]);keys(_).sort((a,b)=>_[a]<_[b]).map(x=>alert(x+':'+_[x]))

Version 5 (without alert; 87 characters)

s.match(_=/\w+/g).map(x=>_[x]=-~_[x]);keys(_).sort((a,b)=>_[a]<_[b]).map(x=>x+':'+_[x])

Version 6 (100 characters)

keys(_,s.match(_=/\w+/g).map(x=>_[x]=-~_[x])).sort((a,b)=>_[a]<_[b]).map(x=>console.log(x+':'+_[x]))

Output:

a:2
31:1
This:1
is:1
text:1
and:1
number:1
\$\endgroup\$
6
  • \$\begingroup\$ You can change _[a] and _[b] to _.a and _.b. Also changing /\w+/g,_={} to _=/\w+/g will produce the same result. \$\endgroup\$
    – eithed
    Jan 29, 2014 at 18:15
  • \$\begingroup\$ @eithedog Thank you! However, I can't change _[a] to be _.a because it tries to access the property "a" of _, not the property a. \$\endgroup\$
    – Toothbrush
    Jan 29, 2014 at 18:33
  • \$\begingroup\$ ah, correct - the order won't be kept. Carry on :) \$\endgroup\$
    – eithed
    Jan 29, 2014 at 19:16
  • \$\begingroup\$ Oh, I didn't notice your answer.. nice. But.. is Object.keys becoming a global in ES6? Your answer seems to assume this, but I don't recall seeing that as scheduled for ES6. \$\endgroup\$
    – FireFly
    Jan 29, 2014 at 21:03
  • \$\begingroup\$ @FireFly I can't find any documentation, but it works fine in Firefox. I haven't tested it in Chrome/Opera/IE. \$\endgroup\$
    – Toothbrush
    Jan 29, 2014 at 21:19
3
\$\begingroup\$

Groovy 77 82

changed regex from [^\w]+ to [^\d\p{L}]+ in order to solve problem with underscore

String s = 'This is a text and a number: 31'

def a=s.split(/[^\d\p{L}]+/) 
a.collectEntries{[it, a.count(it)]}.sort{-it.value}

without first line, 82 characters

output:

[a:2, This:1, is:1, text:1, and:1, number:1, 31:1]
\$\endgroup\$
3
  • \$\begingroup\$ nu_ber is not alphanumeric. This shouls be 2 words \$\endgroup\$
    – Cruncher
    Jan 29, 2014 at 13:52
  • \$\begingroup\$ Why use nu_ber instead of number? \$\endgroup\$ Jan 29, 2014 at 19:42
  • \$\begingroup\$ I was mislead by some other posts ;) now I removed the "_" from the input, but fixed the regex to handle it \$\endgroup\$ Jan 29, 2014 at 20:18
3
\$\begingroup\$

Javascript - 132 126 chars !

(Shortest JS code)

o={},a=[]
for(i in s=s.split(/[\W_]+/))o[z=s[i]]=o[z]+1||1
for(j in o)a.push([j,o[j]])
a.sort(function(b,c){return c[1]-b[1]})

Improved the regex and some edits.


Ungolfed

s = s.split(/[\W_]+/), o={}, a=[]; // split along non-char letters, declare object and array

for (i in s) { n = s[i]; o[n] = o[n] + 1 || 1 } // go through each char and store it's occurence

for (j in o) a.push( [j, o[j]] ); // store in array for sorting

a.sort(function (b, c){ return c[1] - b[1]; }); // sort !

<= // make s = "How shiny is this day is isn't is"

=> [ [ 'is', 3 ],
[ 'How', 1 ],
[ 'shiny', 1 ],
[ 'this', 1 ],
[ 'day', 1 ],
[ 'isn', 1 ],
[ 't', 1 ] ]


Old - 156 143 141 140 132 chars

s=s.split(/[^\w]+/g),o={}
for(i in s){n=s[i];o[n]=o[n]+1||1}a=[]
for(j in o)a.push([j,o[j]])
a.sort(function(b,c){return c[1]-b[1]})

Gave a first try at golfing. Feedback appreciated.

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

EcmaScript 6, 115 100 87 (without prompt&alert)

Thanks to @eithedog:

s.match(/\w+/g,a={}).map(w=>a[w]=-~a[w]),keys(a).map(w=>[w,a[w]]).sort((a,b)=>b[1]-a[1])

With prompt and alert (100):

prompt(a={}).match(/\w+/g).map(w=>a[w]=-~a[w]);alert(keys(a).map(w=>[w,a[w]]).sort((a,b)=>b[1]-a[1]))

Run it in Firefox.

\$\endgroup\$
5
  • 1
    \$\begingroup\$ You don't need var . Also, you can move a={} inside prompt - prompt(a={}). You can also drop Object. and change w=>a[w]=a[w]+1||1 to w=>a[w]=-~a[w] \$\endgroup\$
    – eithed
    Jan 29, 2014 at 17:06
  • \$\begingroup\$ Very nice. Beats the working Python one now :) \$\endgroup\$
    – teh_senaus
    Jan 29, 2014 at 17:36
  • \$\begingroup\$ Same as for @toothbrush's answer - moving the declaration of a from prompt to regexp will spare two more chars. \$\endgroup\$
    – eithed
    Jan 29, 2014 at 18:18
  • \$\begingroup\$ It's nice and clean. Good job! \$\endgroup\$
    – Toothbrush
    Jan 29, 2014 at 19:03
  • \$\begingroup\$ -1. Underscore _ should not be included in a word. \$\endgroup\$ Mar 24, 2014 at 22:27
2
\$\begingroup\$

Ruby 58 82 65

h=Hash.new 0
gets.scan(/[\d\w]+/){h[$&]+=1}
p *h.sort_by{|k,v|-v}

Test run:

$ ruby counttext.rb <<< "This is a text and a number: 31."
["a", 2]
["text", 1]
["This", 1]
["is", 1]
["and", 1]
["number", 1]
["31", 1]

Edit 58->80: Ok, I was way off. I forgot to sort the words by occurrences. Also, Array#uniq is not an enumerator, but uses a given block to compare elements, so passing puts to it didn't filter out duplicates (not that it says that we should).

\$\endgroup\$
8
  • 1
    \$\begingroup\$ Maybe split(/\W+/) instead of scan (untested)? \$\endgroup\$
    – Howard
    Jan 29, 2014 at 8:51
  • \$\begingroup\$ @Howard Thanks. \W excludes _ so that had to be fixed, but it still saved 2 characters (then I added 20 to fix the sorting that I had neglected). \$\endgroup\$
    – daniero
    Jan 29, 2014 at 9:43
  • \$\begingroup\$ Shouldn't be sorted in reverse (a=gets.split(/[_\W]+/)).uniq.map{|w|[w,a.count(w)]}.sort_by(&:last).reverse.map{|x|p x} \$\endgroup\$ Jan 29, 2014 at 10:59
  • \$\begingroup\$ @EduardFlorinescu Nah. reverse is way too verbose ;) Btw, it's not fair changing the question. \$\endgroup\$
    – daniero
    Jan 29, 2014 at 11:47
  • \$\begingroup\$ If you see in the output sample it was sorted descended only that I forgot to specify it. \$\endgroup\$ Jan 29, 2014 at 11:52
2
\$\begingroup\$

F# - 169

let f s=(s+"").Split(set s-set(['a'..'z']@['A'..'Z']@['0'..'9'])|>Set.toArray)|>Seq.where((<>)"")|>Seq.countBy id|>Seq.sortBy((~-)<<snd)|>Seq.iter((<||)(printfn"%s:%d"))

Degolfed:

let count (s : string) =
    s.Split (set s - set (['a'..'z']@['A'..'Z']@['0'..'9']) |> Set.toArray)
 |> Seq.where ((<>) "")
 |> Seq.countBy id
 |> Seq.sortBy ((~-) << snd)
 |> Seq.iter ((<||) (printfn "%s:%d"))

Output when called from fsi:

> "This is a text and a number: 31." |> f
a:2
This:1
is:1
text:1
and:1
number:1
31:1
val it : unit = ()

Update: Some explanation as requested in the comments.

Uses set functions to generate an array of non alphanumeric characters in the input to pass to String.Split, then uses sequence functions to filter out empty strings, generate word counts and print the result.

Some golfing tricks: Adds an empty string to the function argument s to force type inference of the argument as a string rather than explicitly declaring the type. Uses Seq.where rather than Seq.filter to save a few characters (they are synonyms). Mixes forward pipe and ordinary function application in an attempt to minimize characters. Uses currying and (op) syntax to treat <> ~- and <|| operators as regular functions to avoid declaring lambdas to filter empty strings, sort by descending count and print tuples.

\$\endgroup\$
2
  • \$\begingroup\$ You should definitely insert some sort of explanation; that way we can understand your code. \$\endgroup\$
    – Justin
    Jan 30, 2014 at 0:43
  • \$\begingroup\$ Added a degolfed version and some explanation. \$\endgroup\$ Jan 30, 2014 at 5:54
2
\$\begingroup\$

Python - 95 ( now 87 thanks to @primo)

d=__import__('re').findall(r'\w+',raw_input())
print sorted(map(lambda y:(y,d.count(y)),d))

Sample input :

'This is a text and a number: 31'

Sample output :

[('This', 1),('is', 1), ('a', 2),('text', 1),('and', 1),('a', 2),('number', 1),('31', 1)]

Any improvement sugestion would be appreciated

\$\endgroup\$
8
  • 1
    \$\begingroup\$ The solution is nice but the output is not sorted. \$\endgroup\$ Jan 29, 2014 at 14:04
  • \$\begingroup\$ What do you mean by sorted? Thanks for the comment. \$\endgroup\$
    – Azwr
    Jan 29, 2014 at 14:09
  • 1
    \$\begingroup\$ \w matches [a-zA-Z0-9_]. Your entire regex can be replaced by r'\w+'. Also, the x variable is not needed, just use raw_input() as the second parameter to findall. \$\endgroup\$
    – primo
    Jan 29, 2014 at 14:15
  • \$\begingroup\$ By sorted, the OP means that the words that appear most often need to be listed first. Also, your program should include a print statement (i.e. print map(...), otherwise it's not a complete program. \$\endgroup\$
    – primo
    Jan 29, 2014 at 14:22
  • \$\begingroup\$ I don't have time to sort it right now :( I'm in a hurry , thanks for the suggestions and comments. \$\endgroup\$
    – Azwr
    Jan 29, 2014 at 14:24
2
\$\begingroup\$

JavaScript 160 144 (Edited: to meet requirements)

f=Function;o={};s.replace(/\w+/g,f('a','o[a]=++o[a]||1'));Object.keys(o).sort(f('b,c','return o[c]-o[b]')).map(f('k','console.log(k+" "+o[k])'))

Unminified:

f=Function;
o = {};
s.replace(/\w+/g, f('a','o[a]=++o[a]||1'));
Object.keys(o).sort(f('b,c', 'return o[c]-o[b]')).map(f('k','console.log(k+" "+o[k])'))

Logs each word to console in order, passing the following string:

s="This is sam}}ple text 31to test the effectiveness of this code, you can clearly see that this is working-as-intended, but you didn't doubt it did you?.";

Outputs:

you 3
this 2
is 2
can 1
text 1
31to 1
test 1
the 1
effectiveness 1
of 1
This 1
code 1
sam 1
ple 1
clearly 1
see 1
that 1
working 1
as 1
intended 1
but 1
didn 1
t 1
doubt 1
it 1
did 1 

I don't have the heart to use alert().

\$\endgroup\$
6
  • 1
    \$\begingroup\$ The sort should be by the number. of occurrences so you should be first. \$\endgroup\$ Jan 29, 2014 at 16:15
  • \$\begingroup\$ @EduardFlorinescu Silly me... I'll fix it later. \$\endgroup\$ Jan 29, 2014 at 16:56
  • \$\begingroup\$ @EduardFlorinescu fixed \$\endgroup\$ Jan 30, 2014 at 10:22
  • \$\begingroup\$ -1. Underscore _ should not be included in a word. \$\endgroup\$ Mar 24, 2014 at 22:29
  • \$\begingroup\$ ++o[a]||1 => -~o[a] \$\endgroup\$
    – l4m2
    Apr 23, 2018 at 12:06
2
\$\begingroup\$

k [71 chars]

f:{s:" ",x;`_k!m@k:|(!m)@<.:m:#:'=`$1_'(&~((),/:s)like"[a-zA-Z0-9]")_s}

Any other character except alphanumeric chars will be considered as delimiter.

example

f "This is a text and a number: 31."
a     | 2
31    | 1
number| 1
and   | 1
text  | 1
is    | 1
This  | 1

example

f "won't won won-won"
won| 4
t  | 1
\$\endgroup\$
2
\$\begingroup\$

Javascript (135)

u=/\w+/g
for(i=s.length;i--;)for(w in a=s.match(u))u[w=a[w]]=u[w]||a.reduce(function(p,c){return p+=w==c},0)==i&&!console.log(w+":"+i)

Unminified:

u=/\w+/g;for (i=s.length;i--;)
    for(w in a=s.match(u))
        u[w=a[w]] = u[w] || 
           a.reduce(function(p,c){return p+=w==c},0)==i && !console.log(w+":"+i)

Loops over every possible number of matches in descending order, and outputs words with that number of occurrences. Just to be horrible.

Notes: Alert would have reduced the length some. Strictly speaking alphanumeric should be [^\W_]

\$\endgroup\$
2
\$\begingroup\$

Haskell (153 = 104 code + 49 import)

Pretty straight-forward, totally composed function... no argument even necessary! This is my first golf, so go easy, maybe? :)

import Data.Char
import Data.List
import Data.Ord
so=reverse.(sortBy$comparing snd).(map(\t@(x:_)->(x,length t))).group.sort.(map$filter isAlphaNum).words

Output:

*Main> so "This is a text and a number: 31."
[("a",2),("text",1),("number",1),("is",1),("and",1),("This",1),("31",1)]
\$\endgroup\$
2
\$\begingroup\$

q (50)

desc count each group" "vs ssr[;"[^0-9A-Za-z]";" "]
  • ssr replaces non alphanumeric
  • " "vs splits the result into a symbol list
  • count each group counts creates a dict matching distinct elements of the list with the number of occurances
  • desc sorts the dict by descending values

edit: fixed accidentally matching ascii 58-64 and 91-96

\$\endgroup\$
5
  • 1
    \$\begingroup\$ I've no knowledge of q but is the regex [0-z] ASCII-based? If it is, wouldn't it also include ASCII chars 58-64? Because those are : ; < = > ? @. \$\endgroup\$ Feb 3, 2014 at 12:08
  • \$\begingroup\$ Great catch jimbob, thanks \$\endgroup\$ Feb 3, 2014 at 21:40
  • \$\begingroup\$ You're welcome; only spotted because I found the same in C#. Sadly, same with [A-z], which matches ASCII 91-96, which are `[ \ ] ^ _ `` \$\endgroup\$ Feb 3, 2014 at 21:51
  • \$\begingroup\$ ah right you are, nice little ascii lesson right there! \$\endgroup\$ Feb 3, 2014 at 22:23
  • \$\begingroup\$ I just discovered [^_\W]+ for mine, which should be "exclude non-word characters and underscore", if your syntax supports the \W class... \$\endgroup\$ Feb 3, 2014 at 23:09
2
\$\begingroup\$

Pure Bash (no external programs), 164

This is longer than I'd hoped, but I wanted to see if the necessary counting and sorting (in the right direction) could be done purely with bash arrays (associative and non-associative):

declare -A c
for w in ${@//[[:punct:]]/ };{ ((c[$w]++));}
for w in ${!c[@]};{ i=${c[$w]};((m=i>m?i:m));s[$i]+=$w:;}
for((i=m;i>0;i--));{ printf "${s[i]//:/:$i
}";}

Save as a script file, chmod +x, and run:

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

AWK

awk -vRS='[^A-Za-z0-9]' '$0{c[$0]++}END{for(i in c)print c[i]"\t"i": "c[i]|"sort -nr|cut -f2-"}'

Does the job without gawkish extensions:

$ echo 'This is a text and a number: 31.' | awk -vRS='[^A-Za-z0-9]' '$0{c[$0]++}END{for(i in c)print c[i]"\t"i": "c[i]|"sort -nr|cut -f2-"}'
a: 2
This: 1
text: 1
number: 1
is: 1
and: 1
31: 1

If printing "count: word" instead, it would be a bit shorter but I wanted to mimic the given example output...

\$\endgroup\$
2
\$\begingroup\$

Tcl, 99 bytes

proc C s {lmap w [split [regsub -all \[^\\w|\ \] $s {}]] {dict inc D $w}
lsort -s 2 -inde 1 -de $D}

Try it online!

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

Python 2.X (108 - Characters)

print'\n'.join('{}:{}'.format(a,b)for a,b in __import__("collections").Counter(raw_input().split()).items())

Python 3.X (106 - Characters)

print('\n'.join('{}:{}'.format(a,b)for a,b in __import__("collections").Counter(input().split()).items())
\$\endgroup\$
1
  • \$\begingroup\$ Separators will be anything that is not alpha-numeric - You only split on whitespace. \$\endgroup\$
    – daniero
    Jan 29, 2014 at 11:43
1
\$\begingroup\$

Haskell - 137

import Data.List
count text=let textS=(words(text\\".-\':")) in (sortBy (\(_,n) (_,m) -> compare m n)).nub$map(\t->(t,(length.(filter(==t)))textS)) textS
\$\endgroup\$
1
1
\$\begingroup\$

Python 3 - 76

The requirement of splitting on non-alphanumeric chars unfortunately extends the code by 19 chars. The output of the following is shown correctly. If you are not sure, add a .most_common() after the .Counter(...).

i=__import__
print(i('collections').Counter(i('re').findall('\w+',input())))

In/Output

Given the input of This is a text and a number: 31. you get following output:

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

I tried it with other values like

1 2 3 4 5 6 7 8 2 1 5 3 4 6 8 1 3 2 4 6 1 2 8 4 3 1 3 2 5 6 5 4  2 2 4 2 1 3 6

to ensure, the output-order does not rely on the key's value/hash. This example produces:

Counter({'2': 8, '3': 6, '1': 6, '4': 6, '6': 5, '5': 4, '8': 3, '7': 1})

But as I said, print(i('collections').Counter(i('re').findall('\w+',input())).most_common()) would return the results as an definitly ordered list of tuples.


Python 3 - 57 (if a space would be enough for splitting :P)

print(__import__('collections').Counter(input().split()))
\$\endgroup\$
4
  • \$\begingroup\$ If you assumed the string was in some variable s, as some other answers do, you could lose 6 characters by replacing input(). \$\endgroup\$
    – Phil H
    Jan 31, 2014 at 8:24
  • \$\begingroup\$ @PhilH well. you are right, but I would never read that out of the requirements. sure the "string for JavaScript"-part might suggest it, but I cannot, with a clear conscience, interpret a string-variable as a valid "input". But you are right. that would shorten it even more. :P \$\endgroup\$
    – Dave J
    Jan 31, 2014 at 19:43
  • \$\begingroup\$ -1. Underscore _ should not be included in a word. \$\endgroup\$ Mar 24, 2014 at 22:31
  • \$\begingroup\$ Well this depends on the definition of alpha-numeric. In Python, "\w" is defined for accepting alpha-numeric chars. You might be correct but a with this kind of interpretation of the rules, my solution keeps being correct. :) \$\endgroup\$
    – Dave J
    Mar 26, 2014 at 15:20

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.