1,521 bytes · the TypeScript implementation · view raw
/** Only these two are separators: they are how card and sort-code numbers are printed. */function isSeparator(ch: string): boolean {
return ch === " " || ch === "-";
}
/** * Mask all but the last `visible` characters of `value` with `maskChar`. * * Characters are Unicode code points, so an emoji or an accented letter * counts once and the result has as many code points as the input. */exportfunction mask(value: string, visible: number, maskChar: string, keepSeparators: boolean): string {
if (typeof value !== "string") {
thrownew TypeError(`mask needs a string, received ${value}`);
}
if (!Number.isInteger(visible)) {
thrownew TypeError(`visible must be a whole number, received ${visible}`);
}
if (visible < 0) {
thrownew RangeError(`visible must be 0 or greater, received ${visible}`);
}
if (typeof maskChar !== "string" || Array.from(maskChar).length !== 1) {
thrownew RangeError(`maskChar must be exactly one character, received "${maskChar}"`);
}
// Iterating a string yields code points, not UTF-16 units.const chars = Array.from(value);
let shown = 0;
const out: string[] = new Array(chars.length);
// Walk from the end so "the last n" is counted without a second pass.for (let i = chars.length - 1; i >= 0; i--) {
const ch = chars[i];
if (keepSeparators && isSeparator(ch)) {
out[i] = ch;
} elseif (shown < visible) {
out[i] = ch;
shown++;
} else {
out[i] = maskChar;
}
}
return out.join("");
}