Skip to content

Commit 6bf9d07

Browse files
committed
Add string utility functions: ltrim, rtrim, trim (#29)
1 parent 1827861 commit 6bf9d07

2 files changed

Lines changed: 129 additions & 1 deletion

File tree

src/lib/string.spec.ts

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { isNullOrEmpty, isNullOrWhitespace, capitalize, uncapitalize, truncate } from "./string";
1+
import { isNullOrEmpty, isNullOrWhitespace, capitalize, uncapitalize, truncate, ltrim, rtrim, trim } from "./string";
22

33
describe("string tests", () => {
44
test.each([
@@ -120,4 +120,64 @@ describe("string tests", () => {
120120
])("truncate without suffix parameter", (value, maxLength, expected) => {
121121
expect(truncate(value, maxLength)).toBe(expected);
122122
});
123+
124+
test.each([
125+
[null as unknown as string, " ", null],
126+
[undefined as unknown as string, " ", undefined],
127+
[" hello world", " ", "hello world"],
128+
[" hello world", " ", "hello world"],
129+
[" hello world", " ", "hello world"],
130+
])("left trim", (haystack, needle, expected) => {
131+
expect(ltrim(haystack, needle)).toBe(expected);
132+
});
133+
134+
test.each([
135+
[null as unknown as string, " ", null],
136+
[undefined as unknown as string, " ", undefined],
137+
[" hello world", "", " hello world"],
138+
[" hello world", "", " hello world"],
139+
[" hello world", "", " hello world"],
140+
])("left trim without needle", (haystack, needle, expected) => {
141+
expect(ltrim(haystack, needle)).toBe(expected);
142+
});
143+
144+
test.each([
145+
[null as unknown as string, " ", null],
146+
[undefined as unknown as string, " ", undefined],
147+
["hello world ", " ", "hello world"],
148+
["hello world ", " ", "hello world"],
149+
["hello world ", " ", "hello world"],
150+
])("right trim", (haystack, needle, expected) => {
151+
expect(rtrim(haystack, needle)).toBe(expected);
152+
});
153+
154+
test.each([
155+
[null as unknown as string, " ", null],
156+
[undefined as unknown as string, " ", undefined],
157+
["hello world ", "", "hello world "],
158+
["hello world ", "", "hello world "],
159+
["hello world ", "", "hello world "],
160+
])("right trim without needle", (haystack, needle, expected) => {
161+
expect(rtrim(haystack, needle)).toBe(expected);
162+
});
163+
164+
test.each([
165+
[null as unknown as string, " ", null],
166+
[undefined as unknown as string, " ", undefined],
167+
[" hello world ", " ", "hello world"],
168+
[" hello world ", " ", "hello world"],
169+
[" hello world ", " ", "hello world"],
170+
])("trim", (haystack, needle, expected) => {
171+
expect(trim(haystack, needle)).toBe(expected);
172+
});
173+
174+
test.each([
175+
[null as unknown as string, " ", null],
176+
[undefined as unknown as string, " ", undefined],
177+
[" hello world ", "", " hello world "],
178+
[" hello world ", "", " hello world "],
179+
[" hello world ", "", " hello world "],
180+
])("trim without needle", (haystack, needle, expected) => {
181+
expect(trim(haystack, needle)).toBe(expected);
182+
});
123183
});

src/lib/string.ts

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,3 +64,71 @@ export function truncate(value: string | undefined, maxLength: number, suffix =
6464

6565
return `${value.slice(0, maxLength)}${suffix}`;
6666
}
67+
68+
/**
69+
* Removes all occurrences of needle from the start of haystack
70+
* @param haystack string to trim
71+
* @param needle the thing to trim
72+
* @returns the string trimmed from the left side
73+
*/
74+
export function ltrim(haystack: string, needle: string): string {
75+
if (!haystack || !needle) return haystack;
76+
77+
const needleLength = needle.length;
78+
if (needleLength === 0 || haystack.length === 0) {
79+
return haystack;
80+
}
81+
82+
let offset = 0;
83+
84+
while (haystack.indexOf(needle, offset) === offset) {
85+
offset = offset + needleLength;
86+
}
87+
return haystack.slice(offset);
88+
}
89+
90+
/**
91+
* Removes all occurrences of needle from the end of haystack
92+
* @param haystack string to trim
93+
* @param needle the thing to trim
94+
* @returns the string trimmed from the right side
95+
*/
96+
export function rtrim(haystack: string, needle: string): string {
97+
if (!haystack || !needle) {
98+
return haystack;
99+
}
100+
101+
const needleLength = needle.length,
102+
haystackLen = haystack.length;
103+
104+
if (needleLength === 0 || haystackLen === 0) {
105+
return haystack;
106+
}
107+
108+
let offset = haystackLen,
109+
idx = -1;
110+
111+
while (true) {
112+
idx = haystack.lastIndexOf(needle, offset - 1);
113+
if (idx === -1 || idx + needleLength !== offset) {
114+
break;
115+
}
116+
if (idx === 0) {
117+
return "";
118+
}
119+
offset = idx;
120+
}
121+
122+
return haystack.slice(0, offset);
123+
}
124+
125+
/**
126+
* Removes all occurrences of needle from the start and the end of haystack
127+
* @param haystack string to trim
128+
* @param needle the thing to trim
129+
* @returns the string trimmed from the right and left side
130+
*/
131+
export function trim(haystack: string, needle: string): string {
132+
const trimmed = ltrim(haystack, needle);
133+
return rtrim(trimmed, needle);
134+
}

0 commit comments

Comments
 (0)