🎉 Exercism Research is now launched. Help Exercism, help science and have some fun at research.exercism.io 🎉
Avatar of JaeHyoLee

JaeHyoLee's solution

to Word Count in the Lua Track

Published at Jul 13 2018 · 2 comments
Test suite


This solution was written on an old version of Exercism. The tests below might not correspond to the solution code, and the exercise may have changed since this code was written.

Given a phrase, count the occurrences of each word in that phrase.

For example for the input "olly olly in come free"

olly: 2
in: 1
come: 1
free: 1

Running the tests

To run the tests, run the command busted from within the exercise directory.

Further information

For more detailed information about the Lua track, including how to get help if you're having trouble, please visit the exercism.io Lua language page.


This is a classic toy problem, but we were reminded of it by seeing it in the Go Tour.

Submitting Incomplete Solutions

It's possible to submit an incomplete solution so you can see how others have completed the exercise.


local word_count = require('word-count').word_count

describe('word-count', function()
  it('counts one word', function()
    local result = word_count('word')
    local expected = { word = 1 }
    assert.are.same(expected, result)

  it('counts one of each', function()
    local result = word_count('one of each')
    local expected = { one = 1, of = 1, each = 1 }
    assert.are.same(expected, result)

  it('counts multiple occurrences', function()
    local result = word_count('one fish two fish red fish blue fish')
    local expected = { one = 1, fish = 4, two = 1, red = 1, blue = 1 }
    assert.are.same(expected, result)

  it('ignores punctuation', function()
    local result = word_count('car : carpet as java : javascript!!&@$%^&')
    local expected = { car = 1, carpet = 1, as = 1, java = 1, javascript = 1 }
    assert.are.same(expected, result)

  it('includes numbers', function()
    local result = word_count('testing, 1, 2 testing')
    local expected = { testing = 2, ['1'] = 1, ['2'] = 1 }
    assert.are.same(expected, result)

  it('normalizes case', function()
    local result = word_count('go Go GO')
    local expected = { go = 3 }
    assert.are.same(expected, result)
local function update_result(word_table, word_normalized_table, new_word)
  if type(word_table[new_word]) ~= 'nil' then   -- if original word exists either in the table or normalized metatable
    if type(word_table[new_word]) == 'number' then  -- increase count if the word already exists
      word_table[new_word] = word_table[new_word] + 1
    else -- if it does not exist then look into metatable and get the key (word) and increase that word count
      word_table[word_normalized_table[new_word:lower()]] = word_table[word_normalized_table[new_word:lower()]] + 1
  elseif type(word_table[new_word:lower()]) ~= 'nil' then  -- normalized word exists either in the table or normalized metatable
      word_table[word_normalized_table[new_word:lower()]] = word_table[word_normalized_table[new_word:lower()]] + 1
  else -- register word in both original and normalized metatable if it is the first time
    word_table[new_word] = 1
    word_normalized_table[new_word:lower()] = new_word

return {
  word_count = function (source)
  local word_table = {}
  local word_normalized_table = {}
  setmetatable(word_table, {__index = word_normalized_table})

  for new_word in source:gmatch("%w+") do
    update_result(word_table, word_normalized_table, new_word)
  return word_table

Community comments

Find this solution interesting? Ask the author a question to learn more.
Avatar of JaeHyoLee

I may misunderstood the last test in spec file. 'normalizes case' I thought it supposed to treat all mixed lower and upper case letters as same and it should be the first word in the test string. so in my solution it will give different answer for the following case. 'go Go GO' -> go = 3 'Go go GO' -> Go = 3 (since the Go is the first 'go' in the test array) Now I figured out by looking at others' solution.. that 'normalizes case' means all lower case.. so 'Go GO GO' should be go = 3 (at least.. I figured out how to use metatable and method.. though :-))

Avatar of ryanplusplus

Neat solution! But you're right, you only need to make everything lower case :)

What can you learn from this solution?

A huge amount can be learned from reading other people’s code. This is why we wanted to give exercism users the option of making their solutions public.

Here are some questions to help you reflect on this solution and learn the most from it.

  • What compromises have been made?
  • Are there new concepts here that you could read more about to improve your understanding?