js-01-slugify
1.000
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.435355
type: 'test'
...
# Subtest: punctuation collapses and ends are trimmed
ok 2 - punctuation collapses and ends are trimmed
---
duration_ms: 0.145972
type: 'test'
...
# Subtest: already-clean input is unchanged
ok 3 - already-clean input is unchanged
---
duration_ms: 0.046381
type: 'test'
...
# Subtest: digits are preserved
ok 4 - digits are preserved
---
duration_ms: 0.0458
type: 'test'
...
# Subtest: runs of mixed separators collapse to one hyphen
ok 5 - runs of mixed separators collapse to one hyphen
---
duration_ms: 0.074771
type: 'test'
...
# Subtest: leading and trailing junk is trimmed
ok 6 - leading and trailing junk is trimmed
---
duration_ms: 0.048861
type: 'test'
...
# Subtest: all-separator input becomes empty string
ok 7 - all-separator input becomes empty string
---
duration_ms: 0.04111
type: 'test'
...
# Subtest: empty string stays empty
ok 8 - empty string stays empty
---
duration_ms: 0.037421
type: 'test'
...
# Subtest: non-ASCII letters act as separators
ok 9 - non-ASCII letters act as separators
---
duration_ms: 0.054831
type: 'test'
...
1..9
# tests 9
# suites 0
# pass 9
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 31.811967
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.