mirror of
https://github.com/sbrow/strings.git
synced 2025-12-29 23:17:39 -05:00
Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
667937459f | ||
|
|
fa87bbc895 | ||
|
|
83d2ed54be | ||
|
|
72691b6f65 | ||
|
|
1485791367 | ||
|
|
fd15af85cb | ||
|
|
f46b68c709 | ||
|
|
559217ac60 | ||
|
|
ba9656059b |
3
.gitignore
vendored
3
.gitignore
vendored
@@ -1,2 +1,3 @@
|
|||||||
.direnv
|
.direnv
|
||||||
node_modules
|
node_modules
|
||||||
|
yarn-error.log
|
||||||
|
|||||||
21
CHANGELOG.md
21
CHANGELOG.md
@@ -2,6 +2,27 @@
|
|||||||
|
|
||||||
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
|
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
|
||||||
|
|
||||||
|
### [0.1.5](https://github.com/sbrow/strings/compare/v0.1.4...v0.1.5) (2023-06-20)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* Added words and sentences. ([1485791](https://github.com/sbrow/strings/commit/1485791367e68286b48a4fb3d34d42dcaac1e6f5))
|
||||||
|
|
||||||
|
### [0.1.4](https://github.com/sbrow/strings/compare/v0.1.3...v0.1.4) (2023-06-15)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* re-built code. ([f46b68c](https://github.com/sbrow/strings/commit/f46b68c709b16fb67ea6df56271276f31b05a7fd))
|
||||||
|
|
||||||
|
### [0.1.3](https://github.com/sbrow/strings/compare/v0.1.2...v0.1.3) (2023-06-15)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* beforeFirst now returns the input when needle is not present. ([ba96560](https://github.com/sbrow/strings/commit/ba9656059ba14887202463d02a6bca9edb3203a4))
|
||||||
|
|
||||||
### 0.1.2 (2023-05-30)
|
### 0.1.2 (2023-05-30)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -81,7 +81,7 @@ ___
|
|||||||
|
|
||||||
#### Defined in
|
#### Defined in
|
||||||
|
|
||||||
[src/index.ts:34](https://github.com/sbrow/strings/blob/7b676b7/src/index.ts#L34)
|
[src/index.ts:38](https://github.com/sbrow/strings/blob/559217a/src/index.ts#L38)
|
||||||
|
|
||||||
___
|
___
|
||||||
|
|
||||||
@@ -299,7 +299,7 @@ ___
|
|||||||
|
|
||||||
#### Defined in
|
#### Defined in
|
||||||
|
|
||||||
[src/index.ts:35](https://github.com/sbrow/strings/blob/7b676b7/src/index.ts#L35)
|
[src/index.ts:39](https://github.com/sbrow/strings/blob/559217a/src/index.ts#L39)
|
||||||
|
|
||||||
___
|
___
|
||||||
|
|
||||||
|
|||||||
26
flake.nix
26
flake.nix
@@ -8,19 +8,23 @@
|
|||||||
bp.inputs.nixpkgs.follows = "nixpkgs";
|
bp.inputs.nixpkgs.follows = "nixpkgs";
|
||||||
};
|
};
|
||||||
|
|
||||||
outputs = { bp, self, nixpkgs }:
|
outputs = { bp, self, nixpkgs }:
|
||||||
let
|
let
|
||||||
pkgs = nixpkgs.legacyPackages.x86_64-linux;
|
pkgs = nixpkgs.legacyPackages.x86_64-linux;
|
||||||
in {
|
in
|
||||||
devShells.x86_64-linux.default = pkgs.mkShell {
|
{
|
||||||
buildInputs = with pkgs; [
|
devShells.x86_64-linux.default = pkgs.mkShell {
|
||||||
yarn
|
buildInputs = with pkgs; [
|
||||||
];
|
nodejs
|
||||||
};
|
yarn
|
||||||
|
|
||||||
checks.x86_64-linux.default = bp.outputs.legacyPackages.x86_64-linux.buildYarnPackage {
|
nodePackages.typescript-language-server
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
checks.x86_64-linux.default = bp.outputs.legacyPackages.x86_64-linux.buildYarnPackage {
|
||||||
src = ./.;
|
src = ./.;
|
||||||
yarnBuildMore = "yarn build; yarn test";
|
yarnBuildMore = "yarn build; yarn test";
|
||||||
|
};
|
||||||
};
|
};
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|||||||
5
lib/index.d.ts
vendored
5
lib/index.d.ts
vendored
@@ -1,7 +1,12 @@
|
|||||||
|
export * from "./sentences";
|
||||||
|
export * from "./shorten";
|
||||||
export * from "./trim";
|
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 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 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>;
|
export declare const afterFirst: import("ts-toolbelt/out/Function/Curry").Curry<(separator: string, str: string) => string>;
|
||||||
export declare const afterLast: import("ts-toolbelt/out/Function/Curry").Curry<(separator: string, str: string) => string>;
|
export declare const afterLast: import("ts-toolbelt/out/Function/Curry").Curry<(separator: string, str: string) => string>;
|
||||||
export declare const beforeFirst: import("ts-toolbelt/out/Function/Curry").Curry<(separator: string, str: string) => string>;
|
export declare const beforeFirst: import("ts-toolbelt/out/Function/Curry").Curry<(separator: string, str: string) => string>;
|
||||||
export declare const beforeFirstWord: import("ts-toolbelt/out/Function/Curry").Curry<(str: string) => string>;
|
export declare const beforeFirstWord: import("ts-toolbelt/out/Function/Curry").Curry<(str: string) => string>;
|
||||||
|
export declare const afterFirstWord: (str: string) => string;
|
||||||
|
export declare const removeFirstWord: (str: string) => string;
|
||||||
|
|||||||
44
lib/index.js
44
lib/index.js
@@ -14,9 +14,12 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|||||||
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
||||||
};
|
};
|
||||||
Object.defineProperty(exports, "__esModule", { value: true });
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
exports.beforeFirstWord = exports.beforeFirst = exports.afterLast = exports.afterFirst = exports.endsWith = exports.startsWith = void 0;
|
exports.removeFirstWord = exports.afterFirstWord = exports.beforeFirstWord = exports.beforeFirst = exports.afterLast = exports.afterFirst = exports.endsWith = exports.startsWith = void 0;
|
||||||
const utils_1 = require("./utils");
|
const utils_1 = require("./utils");
|
||||||
|
__exportStar(require("./sentences"), exports);
|
||||||
|
__exportStar(require("./shorten"), exports);
|
||||||
__exportStar(require("./trim"), exports);
|
__exportStar(require("./trim"), exports);
|
||||||
|
__exportStar(require("./words"), exports);
|
||||||
function escapeRegExp(str) {
|
function escapeRegExp(str) {
|
||||||
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); // $& means the whole matched string
|
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); // $& means the whole matched string
|
||||||
}
|
}
|
||||||
@@ -24,35 +27,14 @@ exports.startsWith = (0, utils_1.curry)((needle, haystack) => haystack.indexOf(n
|
|||||||
exports.endsWith = (0, utils_1.curry)((needle, haystack) => new RegExp(`${escapeRegExp(needle)}$`).test(haystack));
|
exports.endsWith = (0, utils_1.curry)((needle, haystack) => new RegExp(`${escapeRegExp(needle)}$`).test(haystack));
|
||||||
exports.afterFirst = (0, utils_1.curry)((separator, str) => str.substring(str.indexOf(separator) + 1, str.length));
|
exports.afterFirst = (0, utils_1.curry)((separator, str) => str.substring(str.indexOf(separator) + 1, str.length));
|
||||||
exports.afterLast = (0, utils_1.curry)((separator, str) => str.substring(str.lastIndexOf(separator) + 1, str.length));
|
exports.afterLast = (0, utils_1.curry)((separator, str) => str.substring(str.lastIndexOf(separator) + 1, str.length));
|
||||||
exports.beforeFirst = (0, utils_1.curry)((separator, str) => str.substring(0, str.indexOf(separator)));
|
exports.beforeFirst = (0, utils_1.curry)((separator, str) => {
|
||||||
|
const index = str.indexOf(separator);
|
||||||
|
return index === -1
|
||||||
|
? str
|
||||||
|
: str.substring(0, index);
|
||||||
|
});
|
||||||
// @todo Test
|
// @todo Test
|
||||||
exports.beforeFirstWord = (0, exports.beforeFirst)(" ");
|
exports.beforeFirstWord = (0, exports.beforeFirst)(" ");
|
||||||
// /**
|
// @todo Test
|
||||||
// * @param {String} str
|
exports.afterFirstWord = (0, exports.afterFirst)(" ");
|
||||||
// * @return {String}
|
exports.removeFirstWord = exports.afterFirstWord;
|
||||||
// */
|
|
||||||
// export function afterFirstWord(str) {
|
|
||||||
// return afterFirst(' ', str);
|
|
||||||
// }
|
|
||||||
// export const removeFirstWord = afterFirstWord;
|
|
||||||
// /**
|
|
||||||
// * @param {Number} maxChars
|
|
||||||
// * @param {String} str
|
|
||||||
// * @return {Boolean} False if str is longer than maxChars characters.
|
|
||||||
// */
|
|
||||||
// const shorterThan = curry((maxChars, str) => str.length <= maxChars);
|
|
||||||
// /**
|
|
||||||
// * @param {Number} maxChars The maximum length of the desired output string.
|
|
||||||
// * @param strategy a function that accepts a string and returns a shorter string.
|
|
||||||
// * @return {String} The input string, shortened to maxChars by strategy.
|
|
||||||
// */
|
|
||||||
// const shortenString = (maxChars, strategy) => until(shorterThan(maxChars), strategy);
|
|
||||||
// /**
|
|
||||||
// * @param {Number} maxChars
|
|
||||||
// * @param {String} str The string to remove words from.
|
|
||||||
// * @return {String} the shortened string.
|
|
||||||
// */
|
|
||||||
// export const removeWordsFromStartOfString = uncurryN(
|
|
||||||
// 2,
|
|
||||||
// (maxChars) => shortenString(maxChars, removeFirstWord),
|
|
||||||
// );
|
|
||||||
|
|||||||
3
lib/sentences.d.ts
vendored
Normal file
3
lib/sentences.d.ts
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
export declare function sentencesLazy(str: string, pattern?: RegExp): Generator<string>;
|
||||||
|
export declare function sentences(str: string, pattern?: RegExp): string[];
|
||||||
|
export declare function countSentences(str: string, pattern?: RegExp): number;
|
||||||
26
lib/sentences.js
Normal file
26
lib/sentences.js
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
"use strict";
|
||||||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
|
exports.countSentences = exports.sentences = exports.sentencesLazy = void 0;
|
||||||
|
const matchSentenceEnd = /[!?]|(?<!\be|\be\.g|\betc)\./;
|
||||||
|
function* sentencesLazy(str, pattern = matchSentenceEnd) {
|
||||||
|
if (str.trim() !== '') {
|
||||||
|
const match = str.match(pattern);
|
||||||
|
const index = (match === null || match === void 0 ? void 0 : match.index) ? match.index + 1 : undefined;
|
||||||
|
if (index) {
|
||||||
|
yield str.substring(0, index);
|
||||||
|
yield* sentencesLazy(str.substring(index));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
yield str;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
exports.sentencesLazy = sentencesLazy;
|
||||||
|
function sentences(str, pattern = matchSentenceEnd) {
|
||||||
|
return Array.from(sentencesLazy(str, pattern));
|
||||||
|
}
|
||||||
|
exports.sentences = sentences;
|
||||||
|
function countSentences(str, pattern = matchSentenceEnd) {
|
||||||
|
return sentences(str, pattern).length;
|
||||||
|
}
|
||||||
|
exports.countSentences = countSentences;
|
||||||
3
lib/shorten.d.ts
vendored
Normal file
3
lib/shorten.d.ts
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
export declare const shorterThan: import("ts-toolbelt/out/Function/Curry").Curry<(maxChars: number, str: string) => boolean>;
|
||||||
|
export declare const shorten: import("ts-toolbelt/out/Function/Curry").Curry<(maxChars: number, strategy: any) => (init: unknown) => unknown>;
|
||||||
|
export declare const removeWordsFromBeginning: import("ts-toolbelt/out/Function/Curry").Curry<(maxChars: number, str: string) => unknown>;
|
||||||
14
lib/shorten.js
Normal file
14
lib/shorten.js
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
"use strict";
|
||||||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
|
exports.removeWordsFromBeginning = exports.shorten = exports.shorterThan = void 0;
|
||||||
|
const index_1 = require("./index");
|
||||||
|
const utils_1 = require("./utils");
|
||||||
|
exports.shorterThan = (0, utils_1.curry)((maxChars, str) => str.length <= maxChars);
|
||||||
|
exports.shorten = (0, utils_1.curry)((maxChars, strategy) => (0, utils_1.until)((0, exports.shorterThan)(maxChars), strategy));
|
||||||
|
exports.removeWordsFromBeginning = (0, utils_1.curry)((maxChars, str) => (0, exports.shorten)(maxChars, index_1.removeFirstWord)(str));
|
||||||
|
/*
|
||||||
|
export const removeWordsFromBeginning = uncurryN(
|
||||||
|
2,
|
||||||
|
(maxChars: number) => shorten(maxChars, removeFirstWord),
|
||||||
|
);
|
||||||
|
*/
|
||||||
3
lib/utils.d.ts
vendored
3
lib/utils.d.ts
vendored
@@ -1,5 +1,6 @@
|
|||||||
import type { curry as _curry, when as _when } from "ramda";
|
import type { curry as _curry, until as _until, when as _when } from "ramda";
|
||||||
export declare const curry: typeof _curry;
|
export declare const curry: typeof _curry;
|
||||||
export declare const isEmpty: (str: string) => boolean;
|
export declare const isEmpty: (str: string) => boolean;
|
||||||
export declare const tail: (str: string) => string;
|
export declare const tail: (str: string) => string;
|
||||||
export declare const when: typeof _when;
|
export declare const when: typeof _when;
|
||||||
|
export declare const until: typeof _until;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
"use strict";
|
"use strict";
|
||||||
Object.defineProperty(exports, "__esModule", { value: true });
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
exports.when = exports.tail = exports.isEmpty = exports.curry = void 0;
|
exports.until = exports.when = exports.tail = exports.isEmpty = exports.curry = void 0;
|
||||||
exports.curry = function (fn) {
|
exports.curry = function (fn) {
|
||||||
return (...args) => {
|
return (...args) => {
|
||||||
if (args.length >= fn.length) {
|
if (args.length >= fn.length) {
|
||||||
@@ -17,3 +17,7 @@ exports.tail = tail;
|
|||||||
exports.when = (0, exports.curry)((predicate, whenTrueFn, arg) => {
|
exports.when = (0, exports.curry)((predicate, whenTrueFn, arg) => {
|
||||||
return predicate(arg) ? whenTrueFn(arg) : arg;
|
return predicate(arg) ? whenTrueFn(arg) : arg;
|
||||||
});
|
});
|
||||||
|
exports.until = (0, exports.curry)((predicate, fn, arg) => {
|
||||||
|
const loop = (a) => predicate(a) ? a : loop(fn(a));
|
||||||
|
return loop(arg);
|
||||||
|
});
|
||||||
|
|||||||
3
lib/words.d.ts
vendored
Normal file
3
lib/words.d.ts
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
export declare function wordsLazy(str: string, pattern?: RegExp): Generator<string, void, unknown>;
|
||||||
|
export declare function words(str: string, pattern?: RegExp): string[];
|
||||||
|
export declare function wordCount(str: string, pattern?: RegExp): number;
|
||||||
21
lib/words.js
Normal file
21
lib/words.js
Normal file
@@ -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;
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@sbrow/strings",
|
"name": "@sbrow/strings",
|
||||||
"version": "0.1.2",
|
"version": "0.1.5",
|
||||||
"description": "Library for string manipulation",
|
"description": "Library for string manipulation",
|
||||||
"main": "./lib/index.js",
|
"main": "./lib/index.js",
|
||||||
"types": "./lib/index.d.ts",
|
"types": "./lib/index.d.ts",
|
||||||
@@ -22,6 +22,6 @@
|
|||||||
"typedoc": "^0.24.7",
|
"typedoc": "^0.24.7",
|
||||||
"typedoc-plugin-markdown": "^3.15.3",
|
"typedoc-plugin-markdown": "^3.15.3",
|
||||||
"typescript": "^5.0.4",
|
"typescript": "^5.0.4",
|
||||||
"vitest": "^0.31.1"
|
"vitest": "^2.0.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -71,8 +71,8 @@ describe("strings", () => {
|
|||||||
expect(beforeFirst(" ", "foo bar bat")).toBe("foo");
|
expect(beforeFirst(" ", "foo bar bat")).toBe("foo");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("removes everythin when needle is not present", () => {
|
it("returns the input when needle is not present", () => {
|
||||||
expect(beforeFirst("&", "foo bar bat")).toBe("");
|
expect(beforeFirst("&", "foo bar bat")).toBe("foo bar bat");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
// describe('afterFirstWord', () => {
|
// describe('afterFirstWord', () => {
|
||||||
|
|||||||
12
src/index.ts
12
src/index.ts
@@ -1,7 +1,9 @@
|
|||||||
import { curry } from "./utils";
|
import { curry } from "./utils";
|
||||||
|
|
||||||
|
export * from "./sentences";
|
||||||
export * from "./shorten";
|
export * from "./shorten";
|
||||||
export * from "./trim";
|
export * from "./trim";
|
||||||
|
export * from "./words";
|
||||||
|
|
||||||
function escapeRegExp(str: string) {
|
function escapeRegExp(str: string) {
|
||||||
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); // $& means the whole matched string
|
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); // $& means the whole matched string
|
||||||
@@ -23,9 +25,13 @@ export const afterLast = curry((separator: string, str: string) =>
|
|||||||
str.substring(str.lastIndexOf(separator) + 1, str.length)
|
str.substring(str.lastIndexOf(separator) + 1, str.length)
|
||||||
);
|
);
|
||||||
|
|
||||||
export const beforeFirst = curry((separator: string, str: string) =>
|
export const beforeFirst = curry((separator: string, str: string) => {
|
||||||
str.substring(0, str.indexOf(separator))
|
const index = str.indexOf(separator);
|
||||||
);
|
|
||||||
|
return index === -1
|
||||||
|
? str
|
||||||
|
: str.substring(0, index)
|
||||||
|
});
|
||||||
|
|
||||||
// @todo Test
|
// @todo Test
|
||||||
export const beforeFirstWord = beforeFirst(" ");
|
export const beforeFirstWord = beforeFirst(" ");
|
||||||
|
|||||||
16
src/obfuscate.spec.ts
Normal file
16
src/obfuscate.spec.ts
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import { describe, expect, test } from "vitest";
|
||||||
|
|
||||||
|
import { obfuscate } from "./obfuscate";
|
||||||
|
|
||||||
|
describe('obfuscate', function() {
|
||||||
|
test.each([
|
||||||
|
['brower.spencer@gmail.com', [/^.{3}/, '@', '.com'], ['co'], 'bro•••••••••••@•••••.••m'],
|
||||||
|
['brower.spencer@gmail.com', [/^.{3}/, '@', '.com'], [], 'bro•••••••••••@•••••.com']
|
||||||
|
])('%# works', function (str, allow, deny, want) {
|
||||||
|
const got = obfuscate(str, allow, deny);
|
||||||
|
|
||||||
|
expect(want.length).toEqual(str.length)
|
||||||
|
|
||||||
|
expect(got).toBe(want);
|
||||||
|
});
|
||||||
|
});
|
||||||
56
src/obfuscate.ts
Normal file
56
src/obfuscate.ts
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
export type Patterns = Array<string | RegExp>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Replaces all but the first `length` characters of a string with `•`.
|
||||||
|
*
|
||||||
|
* @param {string} string The string to obfuscate.
|
||||||
|
* @param {string[]} whitelistPatterns
|
||||||
|
* @return {string} The obfuscated string.
|
||||||
|
*/
|
||||||
|
export function obfuscate(
|
||||||
|
string: string,
|
||||||
|
whitelistPatterns: Patterns = [],
|
||||||
|
blacklistPatterns: Patterns = []
|
||||||
|
): string {
|
||||||
|
return [
|
||||||
|
whitelist(whitelistPatterns),
|
||||||
|
blacklist(blacklistPatterns)
|
||||||
|
].reduce(
|
||||||
|
(str, fn) => fn(str),
|
||||||
|
string
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function mergePatterns(patterns: Patterns): string {
|
||||||
|
return patterns
|
||||||
|
.map((pattern) =>
|
||||||
|
typeof pattern === "string" ? escapeRegExp(pattern) : pattern.source
|
||||||
|
)
|
||||||
|
.join("|");
|
||||||
|
}
|
||||||
|
|
||||||
|
function whitelist(patterns: Patterns) {
|
||||||
|
const whitelistPattern = RegExp(mergePatterns(patterns), "dgm");
|
||||||
|
|
||||||
|
return (string: string) =>
|
||||||
|
Array.from(string.matchAll(whitelistPattern))
|
||||||
|
.map((result) => [result[0], result.indices[0][0]])
|
||||||
|
.reduce(
|
||||||
|
(carry, [word, index]) => carry.padEnd(index, "\u2022") + word,
|
||||||
|
""
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function blacklist(patterns: Patterns) {
|
||||||
|
const blacklistPattern = new RegExp(mergePatterns(patterns), "dgm");
|
||||||
|
//return string => Array.from(string.matchAll(blacklistPattern)).filter(x => x[0].length)
|
||||||
|
return (string: string) =>
|
||||||
|
string.replaceAll(blacklistPattern, (match: string) => {
|
||||||
|
return "".padEnd(match.length, "\u2022");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function escapeRegExp(string: string): string {
|
||||||
|
return string.replace(/[.*+?^${}()|[/\]\\]/g, "\\$&");
|
||||||
|
}
|
||||||
|
|
||||||
48
src/sentences.spec.ts
Normal file
48
src/sentences.spec.ts
Normal file
@@ -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);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
23
src/sentences.ts
Normal file
23
src/sentences.ts
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
const matchSentenceEnd = /[!?]|(?<!\be|\be\.g|\betc)\./
|
||||||
|
|
||||||
|
export function* sentencesLazy(str: string, pattern = matchSentenceEnd): Generator<string> {
|
||||||
|
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
|
||||||
|
}
|
||||||
39
src/words.spec.ts
Normal file
39
src/words.spec.ts
Normal file
@@ -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);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
20
src/words.ts
Normal file
20
src/words.ts
Normal file
@@ -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;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user