← Cookbook Strings and text Beginner · Pooka 0.2.0

Count words in a file

Read a text file, split it into words, tally them in a Dict and print the three most common.

You will use

  • pooka/file: file.read returns Result(String, FileError)
  • pooka/string: lowercase, replace, split
  • pooka/dict: dict.empty and the upsert method
  • pooka/list: fold, filter, sort_by, take, each

The whole program

src/main.pooka
module main

import pooka/dict.{Dict}
import pooka/file
import pooka/int
import pooka/io
import pooka/list
import pooka/string

count(words: List(String)) -> Dict(String, Int) {
  list.fold(words, dict.empty(), fn(counts: Dict(String, Int), word: String) {
    counts.upsert(word, fn(seen: Option(Int)) {
      match seen {
        Some(n) -> n + 1
        None -> 1
      }
    })
  })
}

main() {
  match file.read("poem.txt") {
    Ok(text) -> {
      let words = text
      |> string.lowercase
      |> string.replace("\n", " ")
      |> string.split(" ")
      |> list.filter(fn(w: String) { w != "" })
      count(words).to_list()
      |> list.sort_by(fn(a: #(String, Int), b: #(String, Int)) {
        int.compare(b.1, a.1)
      })
      |> list.take(3)
      |> list.each(fn(pair: #(String, Int)) {
        let _ = io.println(pair.0 + ": " + int.to_string(pair.1))
        Nil
      })
    }
    Error(problem) -> {
      let _ = io.eprintln(
        "Could not read " + problem.path + ": " + problem.message
      )
      Nil
    }
  }
}

Run it

Put a few lines in poem.txt next to pooka.toml and run it:

printf 'the owl and the moon\nthe lamp and the owl\nthe end\n' > poem.txt
pooka run
the: 5
and: 2
owl: 2

How it works

  1. Read the file. file.read never throws: a missing file comes back as Error(FileError), carrying the path and a message. The match makes both outcomes explicit.
  2. Make words. Lowercase the text so “The” and “the” count together, turn newlines into spaces, split on spaces, and drop the empty strings left by double spaces.
  3. Tally. count folds over the words with a Dict(String, Int). upsert hands you the current count as an Option: None the first time, Some(n) after.
  4. Rank and print. Turn the dict into pairs, sort by the count (b.1 before a.1 for descending order), take three and print each. io.println returns a Result, so it's discarded with let _ =.

Try next: read the file name from env.args() instead of hard-coding poem.txt, or strip punctuation with a few more string.replace calls.