← run

js-01-slugify

1.000
9/9 tests· basic
Challenge · difficulty 1/5
# Slugify a title

Implement an ES module **`solution.js`** exporting a single function:

```js
export function slugify(input) { /* ... */ }
```

Convert a human title string into a URL slug:

- Lowercase the whole string.
- Treat **any character that is not an ASCII letter (`a–z`) or digit (`0–9`) as a separator** —
  this includes spaces, punctuation, underscores, and non-ASCII letters.
- Replace each **run** of one or more separators with a **single** hyphen (`-`).
- Strip any leading or trailing hyphens from the result.

Return the resulting slug (a string). An input made entirely of separators returns `""`.

Examples:
```js
slugify("Hello World")          // => "hello-world"
slugify("  Hello,  World!  ")   // => "hello-world"
slugify("already-clean")        // => "already-clean"
slugify("Top 10 Tips")          // => "top-10-tips"
slugify("a---b__c")             // => "a-b-c"
slugify("@#$%")                 // => ""
```
tests/solution.test.js
import { test } from "node:test";
import { strict as assert } from "node:assert";
import { slugify } from "./solution.js";

test("spaces become single hyphens", () => {
  assert.equal(slugify("Hello World"), "hello-world");
});

test("punctuation collapses and ends are trimmed", () => {
  assert.equal(slugify("  Hello,  World!  "), "hello-world");
});

test("already-clean input is unchanged", () => {
  assert.equal(slugify("already-clean"), "already-clean");
});

test("digits are preserved", () => {
  assert.equal(slugify("Top 10 Tips"), "top-10-tips");
});

test("runs of mixed separators collapse to one hyphen", () => {
  assert.equal(slugify("a---b__c"), "a-b-c");
});

test("leading and trailing junk is trimmed", () => {
  assert.equal(slugify("--Foo Bar--"), "foo-bar");
});

test("all-separator input becomes empty string", () => {
  assert.equal(slugify("@#$%"), "");
});

test("empty string stays empty", () => {
  assert.equal(slugify(""), "");
});

test("non-ASCII letters act as separators", () => {
  assert.equal(slugify("café crème"), "caf-cr-me");
});
System prompt
You are an expert programmer. Solve the task exactly as specified. Output your solution as fenced code blocks using the required file name(s) and the exact function/type signatures requested. Prefer correctness; do not include prose outside code unless asked.
Proposed solution
```js
export function slugify(input) {
  let result = input.toLowerCase();
  result = result.replace(/[^a-z0-9]/g, ' ');
  result = result.replace(/ +/g, '-');
  result = result.replace(/^-*|-*$/g, '');
  return result;
}
```

Test output (stdout)

TAP version 13
# Subtest: spaces become single hyphens
ok 1 - spaces become single hyphens
  ---
  duration_ms: 0.431491
  type: 'test'
  ...
# Subtest: punctuation collapses and ends are trimmed
ok 2 - punctuation collapses and ends are trimmed
  ---
  duration_ms: 0.06343
  type: 'test'
  ...
# Subtest: already-clean input is unchanged
ok 3 - already-clean input is unchanged
  ---
  duration_ms: 0.04575
  type: 'test'
  ...
# Subtest: digits are preserved
ok 4 - digits are preserved
  ---
  duration_ms: 0.058301
  type: 'test'
  ...
# Subtest: runs of mixed separators collapse to one hyphen
ok 5 - runs of mixed separators collapse to one hyphen
  ---
  duration_ms: 0.075361
  type: 'test'
  ...
# Subtest: leading and trailing junk is trimmed
ok 6 - leading and trailing junk is trimmed
  ---
  duration_ms: 0.04267
  type: 'test'
  ...
# Subtest: all-separator input becomes empty string
ok 7 - all-separator input becomes empty string
  ---
  duration_ms: 0.03943
  type: 'test'
  ...
# Subtest: empty string stays empty
ok 8 - empty string stays empty
  ---
  duration_ms: 0.03607
  type: 'test'
  ...
# Subtest: non-ASCII letters act as separators
ok 9 - non-ASCII letters act as separators
  ---
  duration_ms: 0.05127
  type: 'test'
  ...
1..9
# tests 9
# suites 0
# pass 9
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 27.327092
Reasoning (chain-of-thought)
The task is to implement a `slugify` function that converts a human-readable title string into a URL slug. Let me break down the requirements:

1. Lowercase the whole string
2. Treat any character that is not an ASCII letter (a-z) or digit (0-9) as a separator
3. Replace each run of one or more separators with a single hyphen (-)
4. Strip any leading or trailing hyphens from the result
5. Return the resulting slug (a string)
6. An input made entirely of separators returns ""

Let me think through the implementation:

1. First, convert to lowercase
2. Replace all non-alphanumeric characters with a separator (like a space or hyphen)
3. Replace multiple consecutive separators with a single hyphen
4. Trim leading and trailing hyphens

Here's my approach:

```js
export function slugify(input) {
  // Convert to lowercase
  let result = input.toLowerCase();
  
  // Replace all non-alphanumeric characters with a space
  result = result.replace(/[^a-z0-9]/g, ' ');
  
  // Replace multiple consecutive spaces with a single hyphen
  result = result.replace(/ +/g, '-');
  
  // Trim leading and trailing hyphens
  result = result.replace(/^-*|-*$/g, '');
  
  return result;
}
```

Wait, let me trace through the examples:

1. `slugify("Hello World")` 
   - lowercase: "hello world"
   - replace non-alphanumeric with space: "hello world" (no change)
   - replace multiple spaces with hyphen: "hello-world"
   - trim hyphens: "hello-world" ✓

2. `slugify("  Hello,  World!  ")`
   - lowercase: "  hello,  world!  "
   - replace non-alphanumeric with space: "    hello     world     "
   - replace multiple spaces with hyphen: "hello-world"
   - trim hyphens: "hello-world" ✓

3. `slugify("already-clean")`
   - lowercase: "already-clean"
   - replace non-alphanumeric with space: "already-clean" (no change)
   - replace multiple spaces with hyphen: "already-clean"
   - trim hyphens: "already-clean" ✓

4. `slugify("Top 10 Tips")`
   - lowercase: "top 10 tips"
   - replace non-alphanumeric with space: "top 10 tips" (no change)
   - replace multiple spaces with hyphen: "top-10-tips"
   - trim hyphens: "top-10-tips" ✓

5. `slugify("a---b__c")`
   - lowercase: "a---b__c"
   - replace non-alphanumeric with space: "a   b  c"
   - replace multiple spaces with hyphen: "a-b-c"
   - trim hyphens: "a-b-c" ✓

6. `slugify("@#$%")`
   - lowercase: "@#$%"
   - replace non-alphanumeric with space: "    "
   - replace multiple spaces with hyphen: "-"
   - trim hyphens: "" ✓

All examples check out! The regex approach handles the requirements correctly.

I could also use a different approach with `split` and `filter`, but the regex method is cleaner and more efficient.

```js
export function slugify(input) {
  let result = input.toLowerCase();
  result = result.replace(/[^a-z0-9]/g, ' ');
  result = result.replace(/ +/g, '-');
  result = result.replace(/^-*|-*$/g, '');
  return result;
}
```

This solution is straightforward and handles all the edge cases properly.