← 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
```javascript
export function slugify(input) {
  // Convert to lowercase
  const lower = input.toLowerCase();
  
  // Replace any non-alphanumeric character with a space, then split by spaces
  // and filter out empty strings, then join with hyphens
  const parts = lower.split(/[^a-z0-9]+/).filter(part => part.length > 0);
  
  return parts.join('-');
}
```

Test output (stdout)

TAP version 13
# Subtest: spaces become single hyphens
ok 1 - spaces become single hyphens
  ---
  duration_ms: 0.449471
  type: 'test'
  ...
# Subtest: punctuation collapses and ends are trimmed
ok 2 - punctuation collapses and ends are trimmed
  ---
  duration_ms: 0.06721
  type: 'test'
  ...
# Subtest: already-clean input is unchanged
ok 3 - already-clean input is unchanged
  ---
  duration_ms: 0.04381
  type: 'test'
  ...
# Subtest: digits are preserved
ok 4 - digits are preserved
  ---
  duration_ms: 0.0688
  type: 'test'
  ...
# Subtest: runs of mixed separators collapse to one hyphen
ok 5 - runs of mixed separators collapse to one hyphen
  ---
  duration_ms: 0.08123
  type: 'test'
  ...
# Subtest: leading and trailing junk is trimmed
ok 6 - leading and trailing junk is trimmed
  ---
  duration_ms: 0.04376
  type: 'test'
  ...
# Subtest: all-separator input becomes empty string
ok 7 - all-separator input becomes empty string
  ---
  duration_ms: 0.041371
  type: 'test'
  ...
# Subtest: empty string stays empty
ok 8 - empty string stays empty
  ---
  duration_ms: 0.03529
  type: 'test'
  ...
# Subtest: non-ASCII letters act as separators
ok 9 - non-ASCII letters act as separators
  ---
  duration_ms: 0.05182
  type: 'test'
  ...
1..9
# tests 9
# suites 0
# pass 9
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 27.891753