Skip to content

Instantly share code, notes, and snippets.

@Itsindigo
Last active November 25, 2024 18:45
Show Gist options
  • Save Itsindigo/43713cdf0c0d8000952389ffe9920932 to your computer and use it in GitHub Desktop.
Save Itsindigo/43713cdf0c0d8000952389ffe9920932 to your computer and use it in GitHub Desktop.
Text transformer
import { expect, test, describe } from "vitest";
import { transformText } from "./transformText";
describe("transformText", () => {
test("should handle replacement commands across a word", () => {
expect(transformText("Hello World", "rhllllllrw")).toEqual("hello world");
});
test("handle multiple replace", () => {
expect(transformText("xxx", "3rY")).toEqual("YYY");
});
test("handle multiple replace operations in single command", () => {
expect(transformText("xxxxxx", "3rY3rZ")).toEqual("YYYZZZ");
});
test("move cursor around, with multiple replace operations", () => {
expect(transformText("Hello World", "9lrL7h2rL")).toEqual("HeLLo WorLd");
});
test("replace function when 1 is repetition specified verbosely", () => {
expect(transformText("ABCDEFG", "2l1rX3h1rX")).toEqual("XBXDEFG");
});
test("does not append new characters when repeating beyond original word length", () => {
expect(transformText("AA", "3l5rB")).toEqual("AB");
});
test("a repeat operation must be followed by a replacement character", () => {
expect(() => transformText("AA", "r")).toThrow(
new Error("Invalid `r` operation, no proceeding character")
);
});
});
import { repeat, isNumberChar, getFirstDigitGroup } from "./utils";
/**
* Takes an input string, and a command instruction to mutate the input string.
*
* 1. `h` Move cursor single position to left.
* 2. `l` Move cursor single position to right.
* 3. `r<replacement char>` replace the char at the current cursor position with a new char.
* e.g Given "ABC", rX would mutate the string to "XBC".
*
* Additionally, commands can be repeated by prefixing any command with an integer.
* e.g `2rT` would replace the next 2 characters with the character `T`.
* `9l3h` would move the character 9 positions to the right, and then 3 to the left.
*/
export function transformText(target: string, command: string): string {
const output = target.split("");
let cursor = 0;
for (let i = 0; i < command.length; i++) {
let digitGroup: string;
let nextCommand = command[i];
let repeatOperationNTimes = 1;
if (!nextCommand) {
throw new Error("Assert command not null (for TS)");
}
if (isNumberChar(nextCommand)) {
digitGroup = getFirstDigitGroup(command.slice(i));
i += digitGroup.length;
repeatOperationNTimes = parseInt(digitGroup, 10);
// Set the command operation once number of repetitions is calculated.
nextCommand = command[i];
}
switch (nextCommand) {
case "h":
repeat(() => {
cursor = Math.max(cursor - 1, 0);
}, repeatOperationNTimes);
break;
case "l":
repeat(() => {
cursor = Math.min(cursor + 1, target.length - 1);
}, repeatOperationNTimes);
break;
case "r":
let charToRepeat = command[i + 1];
if (!charToRepeat) {
throw new Error("Invalid `r` operation, no proceeding character");
}
repeat(() => {
/* Do not repeat character beyond original word length */
if (cursor > target.length - 1) {
return;
}
output[cursor] = charToRepeat;
/* Increment cursor to account for digits in the operation command */
if (repeatOperationNTimes > 1 || digitGroup === "1") {
cursor += 1;
}
}, repeatOperationNTimes);
i += 1;
break;
default:
throw new Error(`Unrecognised command operation: ${nextCommand}`);
}
}
return output.join("");
}
export function repeat(operation: () => void, timesToPerform: number) {
while (timesToPerform > 0) {
operation();
timesToPerform -= 1;
}
};
export function getFirstDigitGroup(str: string): string {
let match = str.match(/(\d+)/);
if (!match || !match[1]) {
throw new Error("No digit group found");
}
return match[1];
}
export function isNumberChar(s: string) {
if (s.length !== 1) {
throw new Error(
`Error: isNumberChar asserts a single character is a number "${s}" is ${s.length} characters long.`
);
}
const charCode = s.charCodeAt(0);
return charCode >= 48 && charCode <= 57;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment