~ / track A / clojure basics

Strings and regular expressions

Basic

Strings in Clojure are plain JVM/JS strings, but clojure.string ships a small, consistent vocabulary for working with them — every function takes the string as its first argument, so they thread cleanly with -‌>. Regular expressions are literal values written #"…"; four core functions (r‌e‌-‌f‌i‌n‌d, r‌e‌-‌m‌a‌t‌c‌h‌e‌s, r‌e‌-‌s‌e‌q, r‌e‌-‌p‌a‌t‌t‌e‌r‌n) cover almost every use.

s‌t‌r and the clojure.string toolkit

s‌t‌r is clojure.core's string builder — it concatenates the print representations of its arguments. Use it instead of +:

loading sci
press ⌘/Ctrl-↵ or click ▶ run to evaluate

The clojure.string namespace adds the common transformations. Each takes the string as its first argument so the threading macro -‌> chains them naturally. In a real namespace you'd (:require [clojure.string :as str]) and write str/trim; in the snippets below we use the fully qualified name so each REPL block stays self-contained:

loading sci
press ⌘/Ctrl-↵ or click ▶ run to evaluate

A short tour of the ones you'll reach for first:

loading sci
press ⌘/Ctrl-↵ or click ▶ run to evaluate

s‌p‌l‌i‌t and j‌o‌i‌n are inverse operations: cut a string into a vector of substrings on a separator (regex), or glue a collection back together with one:

loading sci
press ⌘/Ctrl-↵ or click ▶ run to evaluate

r‌e‌p‌l‌a‌c‌e rewrites all matches of a pattern. The pattern can be a literal string or a regex; the replacement can be a string or a function of the match:

loading sci
press ⌘/Ctrl-↵ or click ▶ run to evaluate

r‌e‌p‌l‌a‌c‌e‌-‌f‌i‌r‌s‌t does the same but stops at the first match.

Regex literals — #"…"

#"…" is the Clojure reader's syntax for a compiled regex. The pattern inside is a normal regex string — backslashes need not be doubled inside the #"…" form when written in source code, but inside a JSON/MDX literal you do need to double them, which is why the examples above use \\d+ instead of \d+.

Four core functions consume a regex:

  • r‌e‌-‌f‌i‌n‌d — return the first match (or nil), as a string or vector when groups are present.
  • r‌e‌-‌m‌a‌t‌c‌h‌e‌s — return a match only if the regex matches the entire string. Useful for validation.
  • r‌e‌-‌s‌e‌q — return a lazy seq of all matches.
  • r‌e‌-‌p‌a‌t‌t‌e‌r‌n — build a regex from a string at runtime (rare; literals cover almost every case).
loading sci
press ⌘/Ctrl-↵ or click ▶ run to evaluate

When the regex has capture groups, r‌e‌-‌f‌i‌n‌d and r‌e‌-‌m‌a‌t‌c‌h‌e‌s return a vector: the whole match is index 0, then each group:

loading sci
press ⌘/Ctrl-↵ or click ▶ run to evaluate

This pairs beautifully with destructuring:

loading sci
press ⌘/Ctrl-↵ or click ▶ run to evaluate

Practical example: tokenize and tally

Pull words out of a sentence, lowercase them, and count occurrences. Pure seq + string work, no loops:

loading sci
press ⌘/Ctrl-↵ or click ▶ run to evaluate

r‌e‌-‌s‌e‌q turns the string into a lazy seq of words, f‌r‌e‌q‌u‌e‌n‌c‌i‌e‌s builds the count map, and the threading reads top-to-bottom.

Check yourself

? quiz

What is the difference between `re-find` and `re-matches`?

Exercise

Write a function domain-of that extracts the domain from an email address, returning nil when the input isn't a valid email. Use r‌e‌-‌m‌a‌t‌c‌h‌e‌s so that strings like "ada@example.com extra" are rejected.

loading sci
press ⌘/Ctrl-↵ or click ▶ run to evaluate
 status: new