Skip to content

Commit

Permalink
simplify truncate code
Browse files Browse the repository at this point in the history
  • Loading branch information
Maggi64 committed Feb 10, 2024
1 parent 8ad2a74 commit 38f081c
Show file tree
Hide file tree
Showing 3 changed files with 37 additions and 54 deletions.
6 changes: 3 additions & 3 deletions benchmark/string/truncate.bench.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,17 @@ import { bench, describe } from "vitest";
import { randomStringArray } from "../testData.js";

describe("truncate", () => {
const stringArray = randomStringArray(200);
const stringArray = randomStringArray(500);

bench("moderndash", () => {
for (const str of stringArray) {
truncate(str);
truncate(str, { length: 8 });
}
});

bench("lodash", () => {
for (const str of stringArray) {
lodashVersion(str);
lodashVersion(str, { length: 8 });
}
});
});
77 changes: 30 additions & 47 deletions package/src/string/truncate.ts
Original file line number Diff line number Diff line change
@@ -1,61 +1,44 @@
type Options = {
/**
* The maximum string length.
*
* Default: 30
*/
length?: number;

/**
* The string to indicate text is omitted.
*
* Also named [ellipsis](https://developer.mozilla.org/en-US/docs/Web/CSS/text-overflow)
*
* Default: "...", you might want to use "…" (… U+02026) instead
*/
omission?: string;

/**
* The separator pattern to truncate to.
*
* Default: none
*/
separator?: string;
};

/**
* Truncates a string if it's longer than the given maximum length.
* The last characters of the truncated string are replaced with the omission
* The last characters of the truncated string are replaced with the ellipsis
* string which defaults to "...".
*
* @example
* truncate("Hello, world!", { length: 5 })
* // => "Hello..."
*
* truncate("Hello, world!", { length: 5, ellipsis: " [...]" })
* // => "Hello [...]"
*
* truncate("Hello, world!", { length: 5, separator: " " })
* // => "Hello, ..."
*
* @param str The string to truncate
* @param options The options object
* @param options.length The maximum string length (default: 30)
* @param options.ellipsis The string to indicate text is omitted (default: "...")
* @param options.separator The separator pattern to truncate to (default: none)
* @returns The truncated string
*/
export function truncate(str: string, options?: Options) {
// https://stackoverflow.com/q/1199352
// https://github.com/Maggi64/moderndash/issues/155
// https://lodash.com/docs/4.17.15#truncate

const { length = 30, omission = "...", separator } = options ?? {};
export function truncate(str: string, options?: { length?: number; ellipsis?: string; separator?: string }): string {
const { length = 30, ellipsis = "...", separator } = options ?? {};
if (str.length <= length) return str;

if (str.length <= length) {
return str;
}
const end = length - ellipsis.length;

if (end < 1)
return ellipsis;

// Actually long enough to truncate the string
let truncated = str.slice(0, end);

let maxLength = length - omission.length;
if (maxLength < 0) {
maxLength = 0;
if (separator) {
const sepIndex = truncated.lastIndexOf(separator);
if (sepIndex > -1) {
truncated = truncated.slice(0, sepIndex);
}
}
const subString = str.slice(
0,
// FYI .slice() is OK if maxLength > text.length
maxLength
);

return (
(separator
? subString.slice(0, subString.lastIndexOf(separator))
: subString) + omission
);
return truncated + ellipsis;
}
8 changes: 4 additions & 4 deletions package/test/string/truncate.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,13 @@ it("should truncate string the given length", () => {
});

it("should support a `omission` option", () => {
expect(truncate(string, { omission: " [...]" })).toBe(
expect(truncate(string, { ellipsis: " [...]" })).toBe(
"hi-diddly-ho there, neig [...]"
);
});

it("should support empty `omission` option", () => {
expect(truncate(string, { omission: "" })).toBe(
expect(truncate(string, { ellipsis: "" })).toBe(
"hi-diddly-ho there, neighborin"
);
});
Expand All @@ -41,9 +41,9 @@ it("should support a `separator` option", () => {
});

it("should treat negative `length` as `0`", () => {
[0, -2].forEach((length) => {
for (const length of [0, -2]) {
expect(truncate(string, { length })).toBe("...");
});
}
});

it("should work as an iteratee for methods like `_.map`", () => {
Expand Down

0 comments on commit 38f081c

Please sign in to comment.