diff --git a/lib/index.d.ts b/lib/index.d.ts index 24c0f00..c7f230e 100644 --- a/lib/index.d.ts +++ b/lib/index.d.ts @@ -1,5 +1,7 @@ +export * from "./sentences"; export * from "./shorten"; export * from "./trim"; +export * from "./words"; export declare const startsWith: import("ts-toolbelt/out/Function/Curry").Curry<(needle: string, haystack: string) => boolean>; export declare const endsWith: import("ts-toolbelt/out/Function/Curry").Curry<(needle: string, haystack: string) => boolean>; export declare const afterFirst: import("ts-toolbelt/out/Function/Curry").Curry<(separator: string, str: string) => string>; diff --git a/lib/index.js b/lib/index.js index bc8d3a2..9bbfebd 100644 --- a/lib/index.js +++ b/lib/index.js @@ -16,8 +16,10 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) { Object.defineProperty(exports, "__esModule", { value: true }); exports.removeFirstWord = exports.afterFirstWord = exports.beforeFirstWord = exports.beforeFirst = exports.afterLast = exports.afterFirst = exports.endsWith = exports.startsWith = void 0; const utils_1 = require("./utils"); +__exportStar(require("./sentences"), exports); __exportStar(require("./shorten"), exports); __exportStar(require("./trim"), exports); +__exportStar(require("./words"), exports); function escapeRegExp(str) { return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); // $& means the whole matched string } diff --git a/lib/sentences.d.ts b/lib/sentences.d.ts new file mode 100644 index 0000000..0a59b3c --- /dev/null +++ b/lib/sentences.d.ts @@ -0,0 +1,3 @@ +export declare function sentencesLazy(str: string, pattern?: RegExp): Generator; +export declare function sentences(str: string, pattern?: RegExp): string[]; +export declare function countSentences(str: string, pattern?: RegExp): number; diff --git a/lib/sentences.js b/lib/sentences.js new file mode 100644 index 0000000..70e51d4 --- /dev/null +++ b/lib/sentences.js @@ -0,0 +1,26 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.countSentences = exports.sentences = exports.sentencesLazy = void 0; +const matchSentenceEnd = /[!?]|(?; +export declare function words(str: string, pattern?: RegExp): string[]; +export declare function wordCount(str: string, pattern?: RegExp): number; diff --git a/lib/words.js b/lib/words.js new file mode 100644 index 0000000..cabf247 --- /dev/null +++ b/lib/words.js @@ -0,0 +1,21 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.wordCount = exports.words = exports.wordsLazy = void 0; +const matchWord = /\S+/g; +function* wordsLazy(str, pattern = matchWord) { + for (const match of str.matchAll(pattern)) { + yield match[0]; + } +} +exports.wordsLazy = wordsLazy; +function words(str, pattern = matchWord) { + return Array.from(wordsLazy(str, pattern)); +} +exports.words = words; +function wordCount(str, pattern = matchWord) { + let count = 0; + for (const {} of str.matchAll(pattern)) + count++; + return count; +} +exports.wordCount = wordCount; diff --git a/src/index.ts b/src/index.ts index 0096591..96c00cb 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,7 +1,9 @@ import { curry } from "./utils"; +export * from "./sentences"; export * from "./shorten"; export * from "./trim"; +export * from "./words"; function escapeRegExp(str: string) { return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); // $& means the whole matched string diff --git a/src/sentences.spec.ts b/src/sentences.spec.ts new file mode 100644 index 0000000..0b8b59f --- /dev/null +++ b/src/sentences.spec.ts @@ -0,0 +1,48 @@ +import fc from "fast-check"; +import { describe, expect, it } from "vitest"; + +import { countSentences, sentences } from "./sentences"; + +describe("sentences", () => { + describe("sentences", () => { + it("returns '[]' for empty strings", () => { + expect(sentences("")).toEqual([]); + }); + it.each([ + [" "], + [" "], + [" \r "], + ["\n "], + ])("returns '[]' for strings with only whitespace characters", (str) => { + expect(sentences(str)).toEqual([]); + }); + it.each([ + ["Hello, World!"], + ["Hello, World."], + ["Hello, World?"], + ])("wraps the input in an array if it has only one sentence", (str) => { + expect(sentences(str)).toEqual([str]); + }); + it('passes', () => { + fc.assert( + fc.property(fc.string(), (str) => { + if (str === '' || str.trim() === '') { + expect(sentences(str).length).toEqual(0) + } else if (!str.match(/[!?.]/)){ + expect(sentences(str).length).toEqual(1) + // expect(se??tences(str).length).toEqual(str.split(/\s+/).filter(complement(isEmpty)).length) + } + }) + ); + }) + }); + describe("countSentences", () => { + it("matches the length of the output from sentences", () => { + fc.assert( + fc.property(fc.string(), (str) => { + expect(countSentences(str)).toBe(sentences(str).length); + }) + ); + }); + }); +}); diff --git a/src/sentences.ts b/src/sentences.ts new file mode 100644 index 0000000..3480a94 --- /dev/null +++ b/src/sentences.ts @@ -0,0 +1,23 @@ +const matchSentenceEnd = /[!?]|(? { + if (str.trim() !== '') { + const match = str.match(pattern) + const index = match?.index ? match.index + 1 : undefined + + if (index) { + yield str.substring(0, index) + yield* sentencesLazy(str.substring(index)) + } else { + yield str + } + } +} + +export function sentences(str: string, pattern = matchSentenceEnd) { + return Array.from(sentencesLazy(str, pattern)) +} + +export function countSentences(str: string, pattern = matchSentenceEnd) { + return sentences(str, pattern).length +} \ No newline at end of file diff --git a/src/words.spec.ts b/src/words.spec.ts new file mode 100644 index 0000000..dcfefcc --- /dev/null +++ b/src/words.spec.ts @@ -0,0 +1,39 @@ +import fc from "fast-check"; +import { describe, expect, it } from "vitest"; + +import { wordCount, words } from "./words"; +import { complement, isEmpty } from "ramda"; + +describe("words", () => { + describe("words", () => { + it("returns '[]' for empty strings", () => { + expect(words("")).toEqual([]); + }); + it("returns '[]' for strings with only whitespace characters", () => { + expect(words(" ")).toEqual([]); + expect(words(" ")).toEqual([]); + expect(words(" \r ")).toEqual([]); + expect(words("\n ")).toEqual([]); + }); + it('passes', () => { + fc.assert( + fc.property(fc.string(), (str) => { + if (str === '' || str.trim() === '') { + expect(words(str).length).toEqual(0) + } else { + expect(words(str).length).toEqual(str.split(/\s+/).filter(complement(isEmpty)).length) + } + }) + ); + }) + }); + describe("wordCount", () => { + it("matches the length of the output from words", () => { + fc.assert( + fc.property(fc.string(), (str) => { + expect(wordCount(str)).toBe(words(str).length); + }) + ); + }); + }); +}); diff --git a/src/words.ts b/src/words.ts new file mode 100644 index 0000000..107f3db --- /dev/null +++ b/src/words.ts @@ -0,0 +1,20 @@ +const matchWord = /\S+/g + +export function* wordsLazy(str: string, pattern: RegExp = matchWord) { + for (const match of str.matchAll(pattern)) { + yield match[0] + } +} + +export function words(str: string, pattern: RegExp = matchWord) { + return Array.from(wordsLazy(str, pattern)) +} + +export function wordCount(str: string, pattern: RegExp = matchWord) { + let count = 0 + + for (const {} of str.matchAll(pattern)) + count++ + + return count; +} \ No newline at end of file