What is the most frequent word?

Given a sentence, your program must make its way through it, counting the frequencies of each word, then output the most used word. Because a sentence has no fixed length, and so can get very long, your code must be as short as possible.


  • Each submission should be either a full program or function. If it is a function, it must be runnable by only needing to add the function call to the bottom of the program. Anything else (e.g. headers in C), must be included.
  • There must be a free interpreter/compiler available for your language.
  • If it is possible, provide a link to a site where your program can be tested.
  • Your program must not write anything to STDERR.
  • Your program should take input from STDIN (or the closest alternative in your language).
  • Standard loopholes are forbidden.
  • Your program must be case-insensitive (tHe, The and the all contribute to the count of the).
  • If there is no most frequent word (see test case #3), your program should output nothing.

Definition of a 'word':

You get the list of words by splitting the input text on spaces. The input will never contain any other type of whitespace than plain spaces (in particular no newlines). However, the final words should only contain alphanumerics (a-z, A-Z, 0-9), hyphens (-) and apostrophes ('). You can make that so by removing all other characters or by replacing them by space before doing the word splitting. To remain compatible with previous versions of the rules, apostrophes are not required to be included.

Test Cases

The man walked down the road.
==> the


Slowly, he ate the pie, savoring each delicious bite. He felt like he was truly happy.
==> he


This sentence has no most frequent word.


"That's... that's... that is just terrible!" he said.
==> that's / thats


The old-fashioned man ate an old-fashioned cake.
==> old-fashioned


IPv6 looks great, much better than IPv4, except for the fact that IPv6 has longer addresses.
==> IPv6


This sentence with words has at most two equal most frequent words.

Note: The third and seventh test cases have no output, you may choose either on the fourth.


Programs are scored according to bytes. The usual character set is UTF-8, if you are using another please specify.

When the challenge finishes, the program with the least bytes (it's called ), will win.


To make sure that your answer shows up, please start your answer with a headline, using the following Markdown template:

# Language Name, N bytes

where N is the size of your submission. If you improve your score, you can keep old scores in the headline, by striking them through. For instance:

# Ruby, <s>104</s> <s>101</s> 96 bytes

If there you want to include multiple numbers in your header (e.g. because your score is the sum of two files or you want to list interpreter flag penalties separately), make sure that the actual score is the last number in the header:

# Perl, 43 + 2 (-p flag) = 45 bytes

You can also make the language name a link which will then show up in the leaderboard snippet:

# [><>](http://esolangs.org/wiki/Fish), 121 bytes


Here is a Stack Snippet to generate both a regular leaderboard and an overview of winners by language.

/* Configuration */

var QUESTION_ID = 79576; // Obtain this from the url
// It will be like https://XYZ.stackexchange.com/questions/QUESTION_ID/... on any question page
var ANSWER_FILTER = "!t)IWYnsLAZle2tQ3KqrVveCRJfxcRLe";
var COMMENT_FILTER = "!)Q2B_A2kjfAiU78X(md6BoYk";
var OVERRIDE_USER = 53406; // This should be the user ID of the challenge author.

/* App */

var answers = [], answers_hash, answer_ids, answer_page = 1, more_answers = true, comment_page;

function answersUrl(index) {
  return "https://api.stackexchange.com/2.2/questions/" +  QUESTION_ID + "/answers?page=" + index + "&pagesize=100&order=desc&sort=creation&site=codegolf&filter=" + ANSWER_FILTER;

function commentUrl(index, answers) {
  return "https://api.stackexchange.com/2.2/answers/" + answers.join(';') + "/comments?page=" + index + "&pagesize=100&order=desc&sort=creation&site=codegolf&filter=" + COMMENT_FILTER;

function getAnswers() {
    url: answersUrl(answer_page++),
    method: "get",
    dataType: "jsonp",
    crossDomain: true,
    success: function (data) {
      answers.push.apply(answers, data.items);
      answers_hash = [];
      answer_ids = [];
      data.items.forEach(function(a) {
        a.comments = [];
        var id = +a.share_link.match(/\d+/);
        answers_hash[id] = a;
      if (!data.has_more) more_answers = false;
      comment_page = 1;

function getComments() {
    url: commentUrl(comment_page++, answer_ids),
    method: "get",
    dataType: "jsonp",
    crossDomain: true,
    success: function (data) {
      data.items.forEach(function(c) {
        if (c.owner.user_id === OVERRIDE_USER)
      if (data.has_more) getComments();
      else if (more_answers) getAnswers();
      else process();


var SCORE_REG = /<h\d>\s*([^\n,]*[^\s,]),.*?(\d+)(?=[^\n\d<>]*(?:<(?:s>[^\n<>]*<\/s>|[^\n<>]+>)[^\n\d<>]*)*<\/h\d>)/;

var OVERRIDE_REG = /^Override\s*header:\s*/i;

function getAuthorName(a) {
  return a.owner.display_name;

function process() {
  var valid = [];
  answers.forEach(function(a) {
    var body = a.body;
    a.comments.forEach(function(c) {
        body = '<h1>' + c.body.replace(OVERRIDE_REG, '') + '</h1>';
    var match = body.match(SCORE_REG);
    if (match)
        user: getAuthorName(a),
        size: +match[2],
        language: match[1],
        link: a.share_link,
  valid.sort(function (a, b) {
    var aB = a.size,
        bB = b.size;
    return aB - bB

  var languages = {};
  var place = 1;
  var lastSize = null;
  var lastPlace = 1;
  valid.forEach(function (a) {
    if (a.size != lastSize)
      lastPlace = place;
    lastSize = a.size;
    var answer = jQuery("#answer-template").html();
    answer = answer.replace("{{PLACE}}", lastPlace + ".")
                   .replace("{{NAME}}", a.user)
                   .replace("{{LANGUAGE}}", a.language)
                   .replace("{{SIZE}}", a.size)
                   .replace("{{LINK}}", a.link);
    answer = jQuery(answer);

    var lang = a.language;
    if (/<a/.test(lang)) lang = jQuery(lang).text();
    languages[lang] = languages[lang] || {lang: a.language, user: a.user, size: a.size, link: a.link};

  var langs = [];
  for (var lang in languages)
    if (languages.hasOwnProperty(lang))

  langs.sort(function (a, b) {
    if (a.lang > b.lang) return 1;
    if (a.lang < b.lang) return -1;
    return 0;

  for (var i = 0; i < langs.length; ++i)
    var language = jQuery("#language-template").html();
    var lang = langs[i];
    language = language.replace("{{LANGUAGE}}", lang.lang)
                       .replace("{{NAME}}", lang.user)
                       .replace("{{SIZE}}", lang.size)
                       .replace("{{LINK}}", lang.link);
    language = jQuery(language);

body { text-align: left !important}

#answer-list {
  padding: 10px;
  width: 290px;
  float: left;

#language-list {
  padding: 10px;
  width: 290px;
  float: left;

table thead {
  font-weight: bold;

table td {
  padding: 5px;
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<link rel="stylesheet" type="text/css" href="//cdn.sstatic.net/codegolf/all.css?v=83c949450c8b">
<div id="answer-list">
  <table class="answer-list">
    <tbody id="answers">

<div id="language-list">
  <h2>Winners by Language</h2>
  <table class="language-list">
    <tbody id="languages">

<table style="display: none">
  <tbody id="answer-template">
    <tr><td>{{PLACE}}</td><td>{{NAME}}</td><td>{{LANGUAGE}}</td><td>{{SIZE}}</td><td><a href="{{LINK}}">Link</a></td></tr>
<table style="display: none">
  <tbody id="language-template">
    <tr><td>{{LANGUAGE}}</td><td>{{NAME}}</td><td>{{SIZE}}</td><td><a href="{{LINK}}">Link</a></td></tr>

  • 2
    \$\begingroup\$ Comments are not for extended discussion; this conversation has been moved to chat. \$\endgroup\$ – Doorknob May 11 '16 at 11:28
  • 1
    \$\begingroup\$ So given your new definition of 'word', what is the most common word here don't d'ont dont a a? Would it be dont? \$\endgroup\$ – James May 11 '16 at 15:34
  • \$\begingroup\$ @DrGreenEggsandHamDJ If you have a submission that does remove apostrophes, dont. If not, a. but most submissions do, and so dont is a correct answer. \$\endgroup\$ – George Gibson May 11 '16 at 15:36
  • 1
    \$\begingroup\$ Is the output case-sensitive? So is ipv6 valid output for the last test case? \$\endgroup\$ – kirbyfan64sos May 13 '16 at 22:16
  • 1
    \$\begingroup\$ An extra test case may be of use: "This sentence with words has at most two equal most frequent words." --> <nothing> \$\endgroup\$ – philcolbourn May 14 '16 at 7:12

33 Answers 33


Pyke, 26 25 bytes


Try it here!

Or 23 22 bytes (noncompeting, add node where kills stack if false)


Try it here!

Or with punctuation, 23 bytes (I think this competes? Commit was before the edit)


Try it here!

Or 12 bytes (definitely noncompeting)


Try it here!

l1           -     input.lower()
  .c         -    punc_split(^)
    j        -   j = ^
     .#   )  -  sort(V(i) for i in ^)
       jR/   -   j.count(i)
           e - ^[-1]
  • \$\begingroup\$ Your 23 byte answer would compete if the only punctuation preserved was - and ' (hyphen and apostrophe). \$\endgroup\$ – George Gibson May 18 '16 at 19:42
  • \$\begingroup\$ It only preserves punctuation that isn't at the end of a word \$\endgroup\$ – Blue May 18 '16 at 19:54
  • \$\begingroup\$ Oh, OK (I don't understand Pyke). I guess it competes then... \$\endgroup\$ – George Gibson May 19 '16 at 5:43
  • 1
    \$\begingroup\$ @GeorgeGibson I'm pretty sure the 23 byte version doesn't compete - it could come under standard loopholes. Also I don't expect (m)any people to understand Pyke, I'm making it as my own language \$\endgroup\$ – Blue May 19 '16 at 7:03
  • \$\begingroup\$ Alright then. I think you still win anyway, so it doesn't really matter. \$\endgroup\$ – George Gibson May 19 '16 at 7:04

Jelly, 25 bytes


Try it online! or verify all test cases.


Pyth - 23 30 bytes

There has to be a better way to include digits and hyphens, but I just want to fix this right now.


Test Suite.

  • 1
    \$\begingroup\$ The revised rules require preserving digits and hyphens. \$\endgroup\$ – Dennis May 9 '16 at 0:52
  • \$\begingroup\$ @GeorgeGibson fixed. \$\endgroup\$ – Maltysen May 16 '16 at 19:01

Octave, 115 94 bytes

[a,b,c]=unique(regexp(lower(input('')),'[A-z]*','match'));[~,~,d]=mode(c); try disp(a{d{:}})

Accounts for the case with no most frequent word by using try. In this case it outputs nothing, and "takes a break" until you catch the exception.

Saved 21(!) bytes thanks to Luis Mendo's suggestion (using the third output from mode to get the most common word).

The rules have changed quite a bit since I posted my original answer. I'll look into the regex later.

  • 1
    \$\begingroup\$ you beat me to it, gonna think for something else now. \$\endgroup\$ – Abr001am May 8 '16 at 9:05
  • \$\begingroup\$ Apply mode on c maybe? Its third output gives all tied values, if I recall correctly \$\endgroup\$ – Luis Mendo May 8 '16 at 20:47
  • \$\begingroup\$ I count 115 bytes. \$\endgroup\$ – Conor O'Brien May 8 '16 at 23:34
  • \$\begingroup\$ I believe your regex should be ['\w\d] because you have to preserve apostrophes and digits. Unless those are between upper and lower case in ASCII, in which case ignore me because I don't have a table handy. \$\endgroup\$ – Fund Monica's Lawsuit May 9 '16 at 13:25
  • 1
    \$\begingroup\$ @StewieGriffin [~, ~, out] = mode([1 1 2 2 1 2 3 4 5 5]) gives out = {1 2} \$\endgroup\$ – Luis Mendo May 9 '16 at 23:26

Perl 6, 80 bytes

{$_>1&&.[0].value==.[1].value??""!!.[0].key given .lc.words.Bag.sort:{-.value}}

Let's split the answer into two parts...

given .lc.words.Bag.sort:{-.value}

given is a control statement (like if or for). In Perl 6, they're allowed as postfixes. (a if 1, or like here, foo given 3). given puts its topic (right-hand side) into the special variable $_ for its left-hand side.

The "topic" itself lowercases (lc), splits by word (words), puts the values into a Bag (set with number of occurences), then sorts by value (DESC). Since sort only knows how to operate on lists, the Bag is transformed into a List of Pairs here.


a simple conditional (?? !! are used in Perl 6, instead of ? :).

$_ > 1

Just checks that the list has more than one element.


Accesses to $_ can be shortened... By not specifying the variable. .a is exactly like $_.a. So this is effectively "do both top elements have the same number of occurences" – If so, then we print '' (the empty string).

Otherwise, we print the top element's key (the count): .[0].key.

  • 7
    \$\begingroup\$ It's like half English, half line-noise. Amazing. \$\endgroup\$ – cat May 8 '16 at 15:58
  • 1
    \$\begingroup\$ it's funny how it's the OO-style features that look english-y :P \$\endgroup\$ – Ven May 8 '16 at 16:02
  • 2
    \$\begingroup\$ Also manages to be less readable than Perl 5 while containing more English than Perl 5. D: \$\endgroup\$ – cat May 8 '16 at 16:08
  • 1
    \$\begingroup\$ @cat fixed it -- should be totally unreadable now \$\endgroup\$ – Ven May 8 '16 at 20:19
  • 5
    \$\begingroup\$ value??!! (i know that's a ternary operator, it's just entertaining) \$\endgroup\$ – cat May 8 '16 at 20:26

05AB1E, 30 bytes


lžj¨„ -«Ãð¡©Ùv®yQOˆ}®¯MQÏDg1Q×

Uses CP-1252 encoding. Try it online!.

  • \$\begingroup\$ hmm? \$\endgroup\$ – TessellatingHeckler May 10 '16 at 2:26
  • 3
    \$\begingroup\$ @TessellatingHeckler It only takes one line of input. Unless you repeatedly use the I command, 05AB1E will only take as much as it needs. \$\endgroup\$ – George Gibson May 10 '16 at 6:05

JavaScript (ES6), 155 bytes

s=>(m=new Map,s.toLowerCase().replace(/[^- 0-9A-Z]/gi,'').split(/\ +/).map(w=>m.set(w,-~m.get(w))),[[a,b],[c,d]]=[...m].sort(([a,b],[c,d])=>d-b),b==d?'':a)

Based on @Blue's Python answer.

  • \$\begingroup\$ Your regex replace looks like it drops numbers, and will break the IPv6 test case, is that right? \$\endgroup\$ – TessellatingHeckler May 10 '16 at 2:16
  • \$\begingroup\$ @TessellatingHeckler The definition of word changed since I originally read the question, but I've updated my answer now. \$\endgroup\$ – Neil May 10 '16 at 8:00

Python 3.5, 142 137 134 112 117 110 127 bytes:

(+17 bytes, because apparently even if there are words more frequent than the rest, but they have the same frequency, nothing should still be returned.)

def g(u):import re;q=re.findall(r"\b['\-\w]+\b",u.lower());Q=q.count;D=[*map(Q,{*q})];return['',max(q,key=Q)][1in map(D.count,D)]

Should now satisfy all conditions. This submission assumes that at least 1 word is input.

Try It Online! (Ideone)

Also, if you want one, here is another version of my function devoid of any regular expressions at the cost of about 43 bytes, though this one is non-competitive anyways, so it does not really matter. I just put it here for the heck of it:

def g(u):import re;q=''.join([i for i in u.lower()if i in[*map(chr,range(97,123)),*"'- "]]).split();Q=q.count;D=[*map(Q,{*q})];return['',max(q,key=Q)][1in map(D.count,D)]

Try this New Version Online! (Ideone)

  • \$\begingroup\$ From the challenge comments "if there are two words that are more frequent than the rest, but with the same frequency", the output is 'nothing'. \$\endgroup\$ – RootTwo May 9 '16 at 4:11
  • \$\begingroup\$ @RootTwo Fixed! :) \$\endgroup\$ – R. Kap May 9 '16 at 6:46
  • \$\begingroup\$ @TessellatingHeckler Those are different words though. That's is a contraction for that is whereas thats is not really a word. \$\endgroup\$ – R. Kap May 10 '16 at 3:33
  • \$\begingroup\$ @TessellatingHeckler Can you give me some proof of this comment? Because I am going through all the comments on the post and see no such comment. \$\endgroup\$ – R. Kap May 10 '16 at 3:41

Ruby, 94 92 102 bytes

Gotta go fast (FGITW answer). Returns the word in all uppercase, or nil if there is no most frequent word.

Now updated to new specs, I think. However, I did manage to golf down a little so the byte count is the same!

->s{w=s.upcase.tr("_'",'').scan /[-\w]+/;q=->x{w.count x};(w-[d=w.max_by(&q)]).all?{|e|q[e]<q[d]}?d:p}
  • 5
    \$\begingroup\$ Gotta go fast? \$\endgroup\$ – cat May 8 '16 at 12:37
  • \$\begingroup\$ @cat yeah, 'cuz I was FGITW this time \$\endgroup\$ – Value Ink May 9 '16 at 3:02

Pyth, 32 bytes


Test suite.


JavaScript (ES6), 99 bytes

#input { width: 100%; }
<textarea id="input" oninput="output.innerHTML=F(this.value)"></textarea>
<div id="output"></div>


Sqlserver 2008, 250 bytes

DECLARE @ varchar(max) = 'That''s... that''s... that is just terrible!" he said.';

@ p,@ x
SELECT LEFT(x,k-1),STUFF(x,1,k,'')FROM
c CROSS APPLY(SELECT patindex('%[^a-z''-]%',x+'!')k)k
WHERE''<x)SELECT max(p)FROM(SELECT top 1with ties p
)j HAVING count(*)=1

Try it online!

Sqlserver 2016, 174 bytes

Unable to handle data like this example(counting the equals as 3 words):

DECLARE @ varchar(max) = 'That''s... that''s... that is just terrible!" he said. = = ='

BY value ORDER BY count(*)DESC)x HAVING count(*)=1
  • \$\begingroup\$ I don't like variable approach because it is kind of cheating :) One input -> nothing or something, with set-based approach it has to be longer, because you need to add additional GROUP BY, LEFT JOIN, or PARTITION BY Anyway SQL Server has built in SPLIT function. Ungolfed demo feel free to make it as short as possible. \$\endgroup\$ – lad2025 May 9 '16 at 11:59
  • \$\begingroup\$ @lad2025 thanks alot, didn't know any features from 2016. SPLIT_STRING surely is a long overdue feature. I tried to golf the script down using split, got it down to 174, however it will not be able to filter out text like "= = =" \$\endgroup\$ – t-clausen.dk May 11 '16 at 8:33

PostgreSQL, 246, 245 bytes

WITH z AS(SELECT DISTINCT*,COUNT(*)OVER(PARTITION BY t,m)c FROM i,regexp_split_to_table(translate(lower(t),'.,"''',''),E'\\s+')m)


enter image description here

Input if anyone is interested:


VALUES ('The man walked down the road.'), ('Slowly, he ate the pie, savoring each delicious bite. He felt like he was truly happy.'),
       ('This sentence has no most frequent word.'), ('"That''s... that''s... that is just terrible!" he said. '), ('The old-fashioned man ate an old-fashioned cake.'), 
       ('IPv6 looks great, much better than IPv4, except for the fact that IPv6 has longer addresses.'), ('a   a            a b b b c');

Normally I would use MODE() WITHIN GROUP(...) and it will be much shorter, but it will violate:

If there is no most frequent word (see test case #3), your program should output nothing.


Handling ':

WITH z AS(SELECT DISTINCT*,COUNT(*)OVER(PARTITION BY t,m)c FROM i,regexp_split_to_table(translate(lower(t),'.,"!',''),E'\\s+')m)



║                                              t                                                ║      max      ║
║ a a a b b b c                                                                                 ║               ║
║ The old-fashioned man ate an old-fashioned cake.                                              ║ old-fashioned ║
║ IPv6 looks great, much better than IPv4, except for the fact that IPv6 has longer addresses.  ║ ipv6          ║
║ This sentence has no most frequent word.                                                      ║               ║
║ "That's... that's... that is just terrible!" he said.                                         ║ that's        ║
║ The man walked down the road.                                                                 ║ the           ║
║ Slowly, he ate the pie, savoring each delicious bite. He felt like he was truly happy.        ║ he            ║
  • \$\begingroup\$ could not get as low as you, sqlserver doesn't have build in split yet. However the select part is shorter. \$\endgroup\$ – t-clausen.dk May 9 '16 at 11:17
  • \$\begingroup\$ @GeorgeGibson Sure, fixed + added live demo. \$\endgroup\$ – lad2025 May 11 '16 at 15:30
  • \$\begingroup\$ @lad2025 By common agreement in chat, what you did is no longer necessary, feel free to revert back. \$\endgroup\$ – George Gibson May 11 '16 at 15:32
  • \$\begingroup\$ @GeorgeGibson Yup, edit will be much clear. Live demo is working now, when I wrote answer sqlfiddle was not responding. \$\endgroup\$ – lad2025 May 11 '16 at 15:37

R, 115 bytes

function(s)if(sum(z<-(y=table(tolower((x=strsplit(s,"[^\\w']",,T)[[1]])[x>""])))==max(y))<2)names(which(z))else NULL

This is a function that accepts a string and returns a string if a single word appears more often than others and NULL otherwise. To call it, assign it to a variable.


f <- function(s) {
    # Create a vector of words by splitting the input on characters other
    # than word characters and apostrophes
    v <- (x <- strsplit(s, "[^\\w']", perl = TRUE))[x > ""]

    # Count the occurrences of each lowercased word
    y <- table(tolower(v))

    # Create a logical vector such that elements of `y` which occur most
    # often are `TRUE` and the rest are fase
    z <- y == max(y)

    # If a single word occurs most often, return it, otherwise `NULL`
    if (sum(z) < 2) {
    } else {

Retina, 97 bytes

The rules keep changing...

[^-\w ]

([-\w]+)( \1\b)*
.*\b(\d+);[-\w]+ \1;[-\w]+$


Try it online!

Test suite.

  • 2
    \$\begingroup\$ Fails for this input. \$\endgroup\$ – Conor O'Brien May 8 '16 at 23:24
  • \$\begingroup\$ @CᴏɴᴏʀO'Bʀɪᴇɴ Thanks, fixed. \$\endgroup\$ – Leaky Nun May 8 '16 at 23:39
  • 1
    \$\begingroup\$ And you golfed it 11 bytes ._. impressive \$\endgroup\$ – Conor O'Brien May 8 '16 at 23:41
  • \$\begingroup\$ Also fails for "The old-fashioned man ate an old-fashioned cake." \$\endgroup\$ – t-clausen.dk May 9 '16 at 10:27
  • \$\begingroup\$ This doesn't look right either (expecting a to be the most common word there) \$\endgroup\$ – TessellatingHeckler May 10 '16 at 2:24

Python, 132 bytes

import collections as C,re
def g(s):(a,i),(b,j)=C.Counter(re.sub('[^\w\s-]','',s.lower()).split()).most_common(2);return[a,''][i==j]

Above code assumes that input has at least two words.

  • \$\begingroup\$ Got to love that regex, tho. \$\endgroup\$ – Blue May 9 '16 at 1:52
  • \$\begingroup\$ This is incorrect. The character class \w includes underscores. \$\endgroup\$ – mbomb007 Jan 31 '17 at 17:39

PHP, 223 bytes

$a=array_count_values(array_map(function($s){return preg_replace('/[^A-Za-z0-9]/','',$s);},explode(' ',strtolower($argv[1]))));arsort($a);$c=count($a);$k=array_keys($a);echo($c>0?($c==1?$k[0]:($a[$k[0]]!=$a[$k[1]]?$k[0]:'')):'');

Python 2, 218 bytes

Assumes more than 2 words. Getting rid of punctuation destroyed me...

import string as z
def m(s):a=[w.lower()for w in s.translate(z.maketrans('',''),z.punctuation).split()];a=sorted({w:a.count(w)for w in set(a)}.items(),key=lambda b:b[1],reverse=1);return a[0][0]if a[0][1]>a[1][1]else''
  • \$\begingroup\$ Does this strip ',- etc? \$\endgroup\$ – Tim May 8 '16 at 16:50
  • \$\begingroup\$ @Tim No, I did this challenge before the rules were fully fleshed. Will change. \$\endgroup\$ – Blue May 8 '16 at 16:58
  • \$\begingroup\$ Can you assign the result of sorted to a tuple rather than having to index into the array manually? \$\endgroup\$ – Neil May 8 '16 at 19:18
  • \$\begingroup\$ @Neil you mean just get the first and second items for comparison instead of the entire array? I don't know how to do that \$\endgroup\$ – Blue May 8 '16 at 19:19

Matlab (225)

  • Rules chaneged :/


      function c=f(a),t=@(x)feval(@(y)y(y>32),num2str(lower(x)-0));f=@(x)num2str(nnz(x)+1);e=str2num(regexprep(a,'([\w''-]+)',' ${t($1)} ${f($`)} ${f([$`,$1])}'));[u,r,d]=mode(e);try c=find(e==d{:});c=a((e(c(1)+1)):(e(c(1)+2)));end
  • Toolbox is necessary to run this.

  • How does this work, one of the nicest privileges of regex replace in matlab this it field-executes tokens by calling external-environmental functions parameterized by the tokens caught in the inner environment, so any sequence of "Word_A Word_B .." is replaced by integers "A0 A1 A2 B0 B1 B2 ..." where the first integer is the numerica ascii signature of the word, the second is the starting index, the third is the ending index, these last two integers dont reduplicate in the whole sequence so i took this advantage to transpose it to an array, then mode it then search the result in that array, so the starting/ending indices will consequently follow.

  • Edit: after changing some details, the program is called function by a string parameter.

20 bytes saved thanks to @StewieGriffin, 30 bytes added reproaches to common-agreed loopholes.

  • \$\begingroup\$ You'll have my upvote when you (or someone else) show that this actually works, both for inputs that have a most common word, and for inputs that don't. =) (I can't test it, unfortunately) \$\endgroup\$ – Stewie Griffin May 9 '16 at 11:46
  • \$\begingroup\$ @StewieGriffin i think the programe misbehaves with sentences with equi-frequence words i will fix that \$\endgroup\$ – Abr001am May 9 '16 at 13:29

05AB1E, 22 21 20 bytes

žK„- JÃl#{D.MDgiJëõ?


žK                     # Push [a-zA-Z0-9]
  „-                   # Push 2-char string containing a hyphen and a space
     J                 # Join the stack into a single element
      Ã                # Removes all characters from implicit input except those specified above
       l               # Converts to lowercase
        #              # Split string by spaces
         {             # Sorts array
          D            # Duplicates
           .M          # Finds most common element
             Dg        # Gets length of string without popping
                 iJ    # If length == 1, then convert the array to a string (otherwise the output would be ['example'] instead of example
                   ëõ? # Else push an empty string.

Note: If you're fine with trailing newlines in the output for when you're not supposed to output anything, remove the ? at the end to save a byte.

Note #2: The program will not work with a single word, but I doubt this would be a problem. If you want to fix this, replace # with ð¡ for an extra byte.

05AB1E uses CP-1252 as the charset, not UTF-8.

Try it online!


Perl, 60 56 55 54 bytes

Includes +3 for -p

#!/usr/bin/perl -p

If a word cannot be just a number you can also drop the a for a score of 53.

  • \$\begingroup\$ Does the hyphen in the -anE not count? It does on the other answer (+2 bytes for -p flag)... \$\endgroup\$ – George Gibson May 10 '16 at 17:48
  • \$\begingroup\$ @GeorgeGibson No, see meta.codegolf.stackexchange.com/questions/273/…. The hyphen, the space and the E do not count. The other answer would normally only have to do +1 bytes for -p, but his solution has ' so it cannot be seen as an extension of -e or -E. So he should in fact count +3 (not +2) since he should count the space and the hyphen (but every extra option would only be +1). \$\endgroup\$ – Ton Hospel May 10 '16 at 20:03
  • \$\begingroup\$ @TomHospel Oh, right. \$\endgroup\$ – George Gibson May 11 '16 at 6:06
  • \$\begingroup\$ Is this considered valid given the apostrophe rule? [\pL\d-] looks like it could be shrunken down to [\w-] (unless we care about underscores) but either version will report that instead of that's or thats for test 4. Otherwise, you need to add 4 bytes to insert \x27 in that character class (unless you have a better way of adding an apostrophe). \$\endgroup\$ – Adam Katz Jan 22 '18 at 17:08
  • \$\begingroup\$ @AdamKatz The definition of 'word' changed quite a bit while this was running and I never fully adopted the last version. But to keep you happy I created a fixed (and shorter) version :-). And yes, I do care about underscores \$\endgroup\$ – Ton Hospel Jan 28 '18 at 10:17

PowerShell (v4), 117 bytes

$y,$z=@($input-replace'[^a-z0-9 \n-]'-split'\s'|group|sort Count)[-2,-1]

The first part is easy enough:

  • $input is ~= stdin
  • Regex replace irrelevant characters with nothing, keep newlines so we don't mash two words from the end of a line and the beginning of the next line into one by mistake. (Nobody else has discussed multiple lines, could golf -2 if the input is always a single line).
  • Regex split, Group by frequency (~= Python's collections.Counter), Sort to put most frequent words at the end.
  • PowerShell is case insensitive by default for everything.

Handling if there isn't a most frequent word:

  • Take the last two items [-2,-1] into $y and $z;
  • an N-item list, where N>=2, makes $y and $z the last two items
  • a 1-item list makes $y the last item and $z null
  • an Empty list makes them both null

Use the bool-as-array-index fake-ternary-operator golf (0,1)[truthyvalue], nested, to choose "", $z or $y as output, then take .Name.

PS D:\> "The man walked down the road."|.\test.ps1

PS D:\> "Slowly, he ate the pie, savoring each delicious bite. He felt like he was truly happy."|.\test.ps1

PS D:\> "`"That's... that's... that is just terrible!`" he said."|.\test.ps1

PS D:\> "The old-fashioned man ate an old-fashioned cake."|.\test.ps1

PS D:\> "IPv6 looks great, much better than IPv4, except for the fact that IPv6 has longer addresses."|.\test.ps1

Lua, 232 199 175 bytes

w,m,o={},0;io.read():lower():gsub("[^-%w%s]",""):gsub("[%w-]+",function(x)w[x]=(w[x]or 0)+1 end)for k,v in pairs(w)do if m==v then o=''end if(v>m)then m,o=v,k end end print(o)
  • 1
    \$\begingroup\$ if not w[x]then w[x]=0 end w[x]=w[x]+1 end -> w[x]=(w[x]or0)+1 \$\endgroup\$ – Leaky Nun May 10 '16 at 10:29
  • \$\begingroup\$ if m==v then o=''end -> o=m==v and '' or o \$\endgroup\$ – Leaky Nun May 10 '16 at 13:47

Perl 5, 96 92 84 + 2 (-p flag) = 86 bytes



> echo "The man walked down the road." | perl -p script.pl
  • \$\begingroup\$ Your -p flag should invoke a penalty of 3 bytes. The rules are roughly: Each commandline flag is +1 byte since that is how many extra bytes you need to extend your free -e'code' style commandline. So normally -p is only +1 byte. But here your code has ' so it cannot be run simply from the commandline without escaping. So no combining with -e and the - and the space before the p are extra and must be counted too \$\endgroup\$ – Ton Hospel May 10 '16 at 12:00
  • \$\begingroup\$ @TonHospel Fixed. \$\endgroup\$ – Denis Ibaev May 10 '16 at 13:34
  • \$\begingroup\$ This is actually 84 + 1 (-p flag) if you invoke it on the command line as perl -pe'…' (made available by removing the ' as noted in the first comments) \$\endgroup\$ – Adam Katz Jan 22 '18 at 16:39

Python, 158 bytes

def g(s):import collections as c,re;l=c.Counter(re.sub('[^\w\s-]',"",s.lower()).split());w,f=l.most_common(1)[0];return[w,""][all(f==i[1]for i in l.items())]

Takes its input like this:

g("Bird is the word")

Should match all the requirements, although it does fail on empty strings, is it necessary to check for those? Sorry for the delay.

Advice / feedback / black magic tips for saving bytes are always welcome

  • \$\begingroup\$ Hi, and welcome to PPCG! We score code-golf challenges by the number of bytes in the answer. I went ahead and edited it for you with the correct information. \$\endgroup\$ – Rɪᴋᴇʀ May 9 '16 at 13:56
  • 2
    \$\begingroup\$ Welcome to PPCG! Unfortunately, your submission does not satisfy all the requirements of this challenge as, first of all, it's NOT case insensitive. For instance, it will NOT count occurrences of the word That as occurrences of the word that since the former begins with an uppercase T and the latter begins with a lowercase t. Also, this does NOT remove all other forms of punctuation except hyphens (-) and, optionally, apostrophes (') and as a result, this would NOT work for the fourth test case given in the question. \$\endgroup\$ – R. Kap May 9 '16 at 17:08
  • 1
    \$\begingroup\$ Also, this does NOT output nothing if there is no most frequent word. For instance, using the third test case (This sentence has no most frequent word.) as an example, your function outputs [('This', 1)], when it should instead be outputting nothing. I could go on and on about more issues, so I would recommend fixing them as soon as you can. \$\endgroup\$ – R. Kap May 9 '16 at 18:02
  • \$\begingroup\$ Will do soon, when I have time \$\endgroup\$ – Wouldn't You Like To Know May 11 '16 at 8:20
  • \$\begingroup\$ This is incorrect. The character class \w includes underscores. \$\endgroup\$ – mbomb007 Jan 31 '17 at 17:40

Tcl 8.6, 196 bytes

lmap s [join [read stdin] \ ] {dict incr d [regsub -all {[^\w-]} [string tol $s] {}]}
set y [dict fi $d v [lindex [lsort [dict v $d]] end]]
if {[llength $y]!=2} {set y {}}
puts "==> [lindex $y 0]"

(Alas, I can't figure out how to get it any smaller than that...)


It uses several obscure Tcl idioms to do stuff.

  • [join [read stdin] " "] — input string→list of whitespace-separated words
  • lmap ... — iterate over every element of that list. (Shorter than foreach and effectually identical since the result is discarded.)
  • [regsub ... [string tolower ...]] — Convert the string to lowercase and strip all characters except for word characters and the hyphen.
  • [dict incr d ...] — Create/modify a dictionary/word→count histogram.
  • set y ... — Sort the dictionary values, take the largest one, and return all (key,value) pairs corresponding to it.
  • if... — There must be exactly two elements: a single (key,value) pair, else there is nothing to print.
  • puts... — Print the key in the key value pair, if any. (No word has spaces.)

You can play with it using CodeChef.

  • \$\begingroup\$ 182 \$\endgroup\$ – sergiol Jun 9 '18 at 23:46

Rexx, 109 128 122 bytes

pull s;g.=0;m=0;do i=1 to words(s);w=word(s,i);g.w=g.w+1;if g.w>=m then do;m=g.w;g.m=g.m+1;r=w;end;end;if g.m=1 then say r

Pretty printed...

pull s
do i=1 to words(s)
  if g.w>=m
  then do
if g.m=1 then say r
  • \$\begingroup\$ I don't think this handles all cases of tied most frequent words - see (new) last test case - I made similar mistake. \$\endgroup\$ – philcolbourn May 15 '16 at 21:43
  • \$\begingroup\$ Hopefully, that's fixed it now \$\endgroup\$ – aja May 16 '16 at 11:58

bash, 153 146 131 154 149 137 bytes

declare -iA F
f(){ (((T=++F[$1])==M))&&I=;((T>M))&&M=$T&&I=$1;}
read L
L=${L//[^- a-z0-9]}
printf -vA "f %s;" $L
eval $A;echo $I


declare an associative array F of integers (declare -iA F)

f is a function that, given a word parameter $1, increments frequency count for this word (T=++F[$1]) and compares to max count so far (M).

If equal, the we have a tie so we will not consider this word to be most frequent (I=)

If greater than max count so far (M), then set max count so far to frequency count of this word so far (M=$T) and remember this word (I=$1)

End function f

Read a line (read L) Make lowercase (L=${L,,}) Remove any character except a-z, 0-9, dash(-) and space (L=${L//[^- a-z0-9]}) Make a sequence of bash statements that calls f for each word (printf -vA "f %s;" $L). This is saved to variable A. eval A and print result (eval $a;echo$I)


This quick brown fox jumps over this lazy dog.
This sentence with the words has at most two equal most frequent the words.
The man walked down the road.
This sentence has no most frequent word.
Slowly, he ate the pie, savoring each delicious bite. He felt like he was truly happy.
"That's... that's... that is just terrible!" he said.
The old-fashioned man ate an old-fashioned cake.
IPv6 looks great, much better than IPv4, except for the fact that IPv6 has longer addresses.

Bug: FIXED I have a bug that is not revealed in these test cases. If input is

This sentence with words has at most two equal most frequent words.

then my code should output nothing.

I have a fix but I seem to have hit a bash bug... I get very odd behaviour is M is not declared an integer: ++F[$1]==M (after a few repeated words) increments both F[$1] and M!! - my mistake.


Python 3, 76 98 100 bytes

import re,statistics as S

Try it online

Outputs the most common word as lowercase. Does not include apostrophes because "apostrophes are not required to be included."

statistics.mode requires Python 3.4

Unfortunately, no output to stderr is allowed, or it'd be much shorter.

  • \$\begingroup\$ You're not allowed to print to STDERR, unless this program doesn't produce any error output? \$\endgroup\$ – Okx Jan 31 '17 at 17:17
  • \$\begingroup\$ Your new program doesn't support hyphens! I tried the input i- test i- \$\endgroup\$ – Okx Jan 31 '17 at 17:38
  • \$\begingroup\$ Fixed it all. Still short. \$\endgroup\$ – mbomb007 Jan 31 '17 at 17:54

R, 96 bytes

19 bytes shorter than the existing R answer, with a somewhat different approach.


Reads from stdin, so the input is automatically separated by spaces. We convert to lowercase and use gsub to remove all non-alphanumerics (plus - and '). We count the instances of each word with table and save the result to t. Next, we check if there is more than 1 maximum in t (by seeing if there is more than one element which is equal to max(t). If so, we return the empty string ''. If not, we return the word corresponding to the maximum in t.


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.