Skip to the code
cf.

Truncate at a word boundary

Write truncate(text, maxLength). If text has at most maxLength characters, return it unchanged. Otherwise return a shorter version that ends with … (one character, U+2026) and is at most maxLength characters long, the … included.

Cut between words, never inside one. Words are separated by whitespace: spaces, tabs, and line breaks. Punctuation belongs to the word it touches, so Hello, is one word.

Keep the start of text, exactly as written, up to the end of the last whole word that fits in the first maxLength - 1 characters, and add the …. Whitespace between the kept words stays as it is (a tab stays a tab); whitespace after the last kept word is dropped.

If not even the first word fits, cut it instead: keep the first maxLength - 1 characters and add the …. Characters are counted the way length counts them, and maxLength is at least 1.

truncate("The quick brown fox", 12); // "The quick…"
truncate("The quick brown fox", 16); // "The quick brown…"
truncate("Hello, world", 8);         // "Hello,…"
truncate("Supercalifragilistic", 6); // "Super…"
truncate("Short", 10);               // "Short"

What the tests check

  1. returns text that fits unchanged
    expect(truncate("Hello world", 11)).toBe("Hello world");
    expect(truncate("Hi", 10)).toBe("Hi");
    expect(truncate("", 3)).toBe("");
    
  2. cuts after the last whole word that fits
    expect(truncate("The quick brown fox", 12)).toBe("The quick…");
    expect(truncate("The quick brown fox", 18)).toBe("The quick brown…");
    
  3. keeps a word that ends right at the limit
    expect(truncate("The quick brown fox", 10)).toBe("The quick…");
    expect(truncate("The quick brown fox", 16)).toBe("The quick brown…");
    
  4. never returns more than maxLength characters, the … included
    expect(truncate("The quick brown fox", 15)).toBe("The quick…");
    const text = "The quick brown fox jumps over the lazy dog";
    for (let max = 1; max < text.length; max++) {
      const result = truncate(text, max);
      expect(result.length).toBeLessThanOrEqual(max);
      expect(result.endsWith("…")).toBe(true);
    }
    
  5. splits words at tabs and line breaks too, keeping them as written
    expect(truncate("one two\nthree four", 12)).toBe("one two…");
    expect(truncate("one\ttwo three", 8)).toBe("one\ttwo…");
    
  6. drops the whitespace before the …
    expect(truncate("one   two three", 8)).toBe("one…");
    expect(truncate("one two \n three", 10)).toBe("one two…");
    
  7. keeps punctuation with its word
    expect(truncate("Hello, world", 8)).toBe("Hello,…");
    expect(truncate("Wait... what?", 9)).toBe("Wait...…");
    
  8. cuts the first word when it's too long on its own
    expect(truncate("Supercalifragilistic", 6)).toBe("Super…");
    expect(truncate("Supercalifragilistic is long", 10)).toBe("Supercali…");
    expect(truncate("abc", 1)).toBe("…");
    

Learn more

solution.jsSaved in this browser

Run tests runs every test on your code, here in your browser. The problem is solved when they all pass.

Ctrl + Enter runs the tests (⌘ + Enter on a Mac).