Given a string as input, determine whether it is a noun or not.

You will be scored on the 1000 most common English words, by how many you correctly label as a noun or not.

The program or function which correctly classifies the most of those words in 50 bytes or less will win.


A noun is a word that represents a thing, typically. It gets more complex, but that's the basic idea.

In cases where a word could either be a noun or some other part of speech, I classified it as a noun, even if that's a rare usage. Or actually, I let this site do it for me.

The words you'll be scored on are these 1000 common words, which are from simple Wikipedia, with "two" and "once" added. Of those, these are the 586 nouns, and these are the 414 non-nouns. You can find all three lists here. Note that all of these inputs are in lower case. These lists are final - don't try to argue grammar.

Your program will be considered correct if it outputs a truthy result on an input which is a noun, and a falsy result on an input which is not a noun.


Programs must have a deterministic output. If you want to use randomness, seed it. Programs are not allowed to use built-in noun lists or other built-in part-of-speech functionality.


a: noun
act: noun
active: noun
about: non-noun
above: non-noun
across: non-noun

Please indicate what your program's success rate is in your answer. The program or function of at most 50 bytes with the highest success rate wins. In case of a tie, lowest byte count will determine a winner. Good luck!


8 Answers 8


JavaScript (ES6), 43 bytes, 622 630 633

Just to get the ball rolling. Returns 1 for nouns, 0 for non-nouns.



We bet on noun if both following conditions are met:

  1. The word length is 3, 4, 5, 6, 7, 8 or 11. This is done by right-shifting the binary number 100111111000 (2552 as decimal).
  2. The word starts with one of these letters: bcdfghijklmpqrstvwy
  • \$\begingroup\$ Just as I was about to comment, with JS specifically in mind, that the byte limit was far too restrictive, you post this! I was thinking, without having looked at the list, that a better score than 586 might just be possible by testing the first letter or 2 in each word. Nicely done :) \$\endgroup\$
    – Shaggy
    Commented Aug 12, 2017 at 21:39
  • \$\begingroup\$ An explanation would be nice, for people less familiar with Javascript. As far as I can tell, this checks whether the word length is 3, 4, 5, 6, 7, 8 or 11, and the word also starts with one of a set of letters? \$\endgroup\$
    – isaacg
    Commented Aug 12, 2017 at 22:01
  • \$\begingroup\$ @isaacg That's correct. Explanation added. \$\endgroup\$
    – Arnauld
    Commented Aug 12, 2017 at 22:14
  • 4
    \$\begingroup\$ Note that the character class [bcdf-mp-tvwy] is equivalent to the class [^aenouxz]. A change would save 4 bytes, which could be capitalized on. \$\endgroup\$ Commented Aug 12, 2017 at 22:14
  • \$\begingroup\$ @fireflame241 Very true. And that can even be shortened to [^aenouz] because we don't have any word starting with a x. \$\endgroup\$
    – Arnauld
    Commented Aug 12, 2017 at 22:19

JavaScript (ES6), 50 bytes, score 693


Just looking for any possible patterns that non-nouns have that nouns don't.

Non-nouns more often contain:

  1. a, o, u, or z as the first letter.
  2. th as the first two letters.
  3. Two letters only. [Think pronouns (me, we, us, he, it) and prepositions (of, to, in, on, by, at, up, ...).]
  4. e, followed by one or more letters, followed by e or y.
  5. f, l, or o, followed by any letter, followed by r.
  6. a, followed by any letter, followed by p.


var nouns = ['a','act','active','activity','age','air','amount','answer','anything','apple','area','arm','army','art','ask','attack',


tnoun = 0;
nouns.forEach(s=>tnoun += f(s));
console.log('Nouns: ' + tnoun + ' correct');

tnonNoun = 0;
nonNouns.forEach(s=>tnonNoun += !f(s));
console.log('Non-nouns: ' + tnonNoun + ' correct');
console.log('Total: ' + (tnoun + tnonNoun) + ' correct');

  • \$\begingroup\$ I believe you can save a byte by changing the first regex to /h|n/ (or by doing /^.[hn]/.test(s)), and another by changing s[2]>'' to either !!s[2] or 2 in s. \$\endgroup\$ Commented Aug 13, 2017 at 2:47
  • \$\begingroup\$ Thanks, @ETHproductions. I can use your suggestions and combine the two tests to save a bunch of bytes, which allowed me to add code to improve my score. \$\endgroup\$ Commented Aug 13, 2017 at 4:04
  • \$\begingroup\$ Isn't a.p redundant since you already have [aouz]? \$\endgroup\$ Commented Aug 14, 2017 at 13:31
  • \$\begingroup\$ @AdmBorkBork, the a in [aouz] is matched only when at the beginning of the string. For whatever reason, testing for a.p anywhere in the string improves the score. \$\endgroup\$ Commented Aug 14, 2017 at 13:53

Jelly, 48 bytes, score 731

This is my first ever answer in Jelly and I went to a lot of trouble putting this together. Ah well ... that was fun. :-)


1 byte saved thanks to @JonathanAllan

Try it online!

Breakdown and test suites

  • Non-nouns correctly identified as non-nouns: 265 / 414 (64%)
  • Nouns correctly identified as nouns: 466 / 586 (79.5%)


We first compute a hash of the input string by:

  • converting it to an integer by interpreting each code point as a base-256 digit
  • applying modulo 4080 (chosen as the most efficient value with no more than 12 bits)
  • keeping the 8 most significant bits of the result

This leaves us with an index in [0 ... 255] and thus divides all words into 256 groups.

For each group of words, we pre-compute a binary flag which is 1 if the group contains more nouns than non-nouns, and 0 otherwise. This leads to a 256-bit number N that we're going to use as a lookup-table. We store it as a base-250 encoded string.

Below is the binary representation of N.


Which can be stored as “Ạ$ⱮẊḲḲLÑMṆụ⁻ẉṂ`ŻvḤæɠ5ṭȯƁU*×TdƲḥ`’ in Jelly.

Hence the code:

O‘ḅ⁹%⁽€Oæ»4“Ạ$ⱮẊḲḲLÑMṆụ⁻ẉṂ`ŻvḤæɠ5ṭȯƁU*×TdƲḥ`’æ»Ḃ    main link

O                                                   convert the input string to a list of
                                                    code points
 ‘                                                  increment each of them
  ḅ⁹                                                convert from base 256 to an integer
    %⁽€O                                            modulo 4080
        æ»4                                         drop the 4 least significant bits
           “Ạ$ⱮẊḲḲLÑMṆụ⁻ẉṂ`ŻvḤæɠ5ṭȯƁU*×TdƲḥ`’æ»     right shift N by this amount
                                               Ḃ    test the least significant bit
  • \$\begingroup\$ Nice job! Save a byte to boot with O‘ḅ⁹%⁽€Oæ»4“Ạ$ⱮẊḲḲLÑMṆụ⁻ẉṂ`ŻvḤæɠ5ṭȯƁU*×TdƲḥ`’æ»Ḃ (also note you can use the footer on TIO, I'd go with Ç€¬S,L and Ç€S,L for your two test suites. \$\endgroup\$ Commented Aug 14, 2017 at 20:38
  • \$\begingroup\$ @JonathanAllan Thanks for the tips! \$\endgroup\$
    – Arnauld
    Commented Aug 14, 2017 at 21:16

Jelly, 50 bytes, score 763

Using a hash now (much like Arnauld's Jelly answer)


Try It Online!

250 / 414 for Non-Nouns
513 / 586 for Nouns
Total = 250 + 513 = 763.


Builds a table with 308 entries, either 1 (identifying a noun) or 0 (identifying a non noun) and indexes into it using a key provided by a hash function that utilises the product of the ordinals of the input word:

OP%⁽Wpị“!ḋGẠ⁻Ṭȥʋt|Ḥ\⁾°½İ@G2ḂƑ½ịʂ¶ɦḲ⁷³Hz~⁵p9)⁹ƙ¿’B¤ - Link: list of characters, word
O                                                  - convert to ordinals
 P                                                 - product
   ⁽Wp                                             - base 250 number = 22863
  %                                                - modulo (by 22863)
                                                 ¤ - nilad plus link(s) as a nilad:
       “!ḋGẠ⁻Ṭȥʋt|Ḥ\⁾°½İ@G2ḂƑ½ịʂ¶ɦḲ⁷³Hz~⁵p9)⁹ƙ¿’   -   base 250 number
                                                B  -   as a binary list (308 bits)
      ị                                            - index into (1-indexed and modular,
                                                  -   so adds another modulo by 308)

Previous:  50  47 bytes, score 684


A monadic link taking a word and returning a list of one character (truthy) if the word is identified as a noun, or an empty list or zero (both falsey) if it is not.

Try it online! (the footer performs an if else on the result to print Noun or Non-Noun)
...or see the scoring program (counts up truthy indexes across the two lists and then calculates the score).

Score breakdown: 462 / 586 nouns correctly identified (124 incorrect), 222 / 414 non-nouns correctly identified (192 incorrect) -- total correct = 684 / 1000.


Guess it is not a noun if...

  • the last character and the character two before that are equal (with modular and 1-based indexing)
  • either of the first two length 2 substrings are in:
    'be', 'th', 'le', 'he', 'm ', 'ev', 'et', 's ', 'fl', 'ax', 'en', 'fo', 'am', 'az' (note: 'm ' and 's ' are only here to ease compression, but they never appear anyway)
  • The -299th index (with modular and 1-based indexing) is any of:
    aenouyz (although this is implemented inversely and with excess capital letters)
    ...since the words all have length between 1 and 11 the -299th index is equivalent to using the length to index mapping: {7:2; 8:5; 9:7; 11:9; else 1}

ḣ3Ẇf“QṘ°ḂżÐŒ#ḍæ09»s2¤Ȧ¬ȧØY⁾niyṖf⁽ż2ị$ - Link 1: list of characters, word
ḣ3                                    - head to index 3 (1st 3 characters, like 'abc')
  Ẇ                                   - all sublists (['a','b','c','ab','bc','abc']
                    ¤                 - nilad followed by link(s) as a nilad:
    “QṘ°ḂżÐŒ#ḍæ09»                    - compression of "bethlehem evets flaxenfoamaz"
                  s2                  - split into chunks of 2:
                                      -   be,th,le,he,m ,ev,et,s ,fl,ax,en,fo,am,az
   f                                  - filter keep (can only match 'ab' or 'bc')
                     Ȧ                - any and all (0 if empty, 1 if not)
                      ¬               - logical not
                        ØY            - consonant -y yield = "BCD...WXZbcd...wxz"
                          ⁾ni         - character pair = "ni" (no shrubbery for you!)
                             y        - translate (exchange the n for an i)
                              Ṗ       - pop (remove the z)
                       ȧ              - logical and
                                    $ - last two links as a monad:
                                ⁽ż2   -   base 250 literal = -299
                                   ị  -   index into the word
                               f      - filter keep

0,-2ịE¬ȧÇ - Main link: list of characters, word
0,-2      - pair zero with -2 = [0,-2]
    ị     - index into the word (last character and the one before the one before that)
     E    - all (both) equal?
      ¬   - logical not
        Ç - call the last link (1) as a monad
       ȧ  - logical and

13 bytes, score: 638

A first quick bash (extended above)

  • \$\begingroup\$ 0,-2 doesn't mean pair zero with -2 it means literal [0, -2] \$\endgroup\$ Commented Aug 13, 2017 at 10:47
  • \$\begingroup\$ But it's the very same effect :p \$\endgroup\$ Commented Aug 13, 2017 at 10:48
  • \$\begingroup\$ no it's not 0,-2 is a nilad, not separate (0)(,)(-2)...of course it's the same effect in this case but not always. I learned that the hard way...and whatever the case I'd anyways prefer to explain what actually happens instead of something with the same effect or something. \$\endgroup\$ Commented Aug 13, 2017 at 10:49
  • \$\begingroup\$ If I'd written "join" rather than "pair" would you have commented "no join is j"? \$\endgroup\$ Commented Aug 13, 2017 at 10:56
  • \$\begingroup\$ I might be a little bit pedantic, but pair or join are obviously wrong ways to phrase it, since 0,-2,-6 for example doesn't mean pair 0 with -2 and then pair that with -6 = [[0, -2], -6] but it rather means literal [0, -2, -6]. I get it, the , atom and the ...,...(,...(...)) literal are confusing...but stilll 0,-2,-6 isn't quite the same as 0,-2;-6 since the former is 1 link and the latter is 3 links. \$\endgroup\$ Commented Aug 13, 2017 at 10:59

Julia 34bytes, 609


I wanted to save on characters by using the in-built hash. I feel like there must be a way to do this better. Julia is just not friendly enough with the bit-banging operations I want to use to make this better I think.

Finding suitable bitmasks for the hash to separate them, is an interesting game.

  • \$\begingroup\$ Best solution ;) \$\endgroup\$
    – tamasgal
    Commented Aug 15, 2017 at 18:58

Python 2, 50 bytes, accuracy: 596

lambda x:2<len(x)<7 or x[0]in"abcgmprs"or"st" in x

Try it online!

Simply checks first letter, length, and whether "st" is in the word Code assumes that word is defined as x (Edit: Thanks to issacg for fixing code from snippet to function)

  • \$\begingroup\$ Hi, welcome to the site. While this is interesting, submissions are required to either be fuctions or full programs. This is a snippet, which is not allowed. See this Try it online! link for a way to convert this snippet to a function while still executing the same code. \$\endgroup\$
    – isaacg
    Commented Aug 17, 2017 at 20:31

Haskell, 36 bytes, 626 631

f x=length x>2&&x!!0`notElem`"aenou"
  • \$\begingroup\$ 643 if anyone has a shorter language: length x>2&&(x!!0`notElem`"aenou"||x!!1`elem`"acqrsty") \$\endgroup\$
    – BlackCap
    Commented Aug 18, 2017 at 14:15

Python 3, 50 bytes, score 602

Python isn't the most verbose language, but 50 bytes is tough.

lambda x:all(x.count(y)<1for y in["ful","y","er"])

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.