Skip to content

Commit

Permalink
Rebuild with Symbol-based approach.
Browse files Browse the repository at this point in the history
  • Loading branch information
alexmacarthur committed Mar 20, 2022
1 parent 2577020 commit 974258f
Show file tree
Hide file tree
Showing 18 changed files with 215 additions and 333 deletions.
10 changes: 3 additions & 7 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,17 @@
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="favicon.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite App</title>
</head>
<body>
<script type="module">
import striff from "/src/index.ts"

let str1 = "abc";
let str2 = "xyz12";
console.log(striff(str1, str2));
let str1 = "abbbc";
let str2 = "ab";

// str1 = "abbbc";
// str2 = "bc";
// console.log(striff(str1, str2));
console.log(striff(str1, str2));
</script>
</body>
</html>
22 changes: 13 additions & 9 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 8 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
{
"name": "striff",
"description": "Simple string diffing.",
"description": "Real simple string diffing.",
"author": "Alex MacArthur (https://macarthur.me)",
"version": "0.0.5",
"version": "1.0.0",
"main": "dist/index.umd.js",
"module": "dist/index.es.js",
"types": "dist/index.d.ts",
"license": "MIT",
"keywords": [
"string diffing",
"difference",
"differ"
],
"files": [
"src/",
"dist/"
Expand All @@ -20,7 +25,7 @@
"devDependencies": {
"@types/jest": "^27.4.1",
"jest": "^27.5.1",
"prettier": "^2.5.1",
"prettier": "^2.6.0",
"ts-jest": "^27.1.3",
"typescript": "^4.6.2",
"vite": "^2.8.6"
Expand Down
42 changes: 21 additions & 21 deletions src/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,14 @@ describe("characters are added", () => {
const { added, removed } = striff("abc", "abcd");

expect(removed).toHaveLength(0);
expect(added).toEqual([{ character: "d", index: 3 }]);
expect(added).toEqual([{ value: "d", index: 3 }]);
});

it("Correctly diffs when characters are added to middle.", () => {
const { added, removed } = striff("hi pal", "hi, pal");

expect(removed).toHaveLength(0);
expect(added).toEqual([{ character: ",", index: 2}]);
expect(added).toEqual([{ value: ",", index: 2}]);
});
});

Expand All @@ -31,11 +31,11 @@ describe("characters are removed", () => {
expect(removed).toHaveLength(2);
expect(removed).toEqual([
{
character: "b",
value: "b",
index: 1,
},
{
character: "c",
value: "c",
index: 2,
},
]);
Expand All @@ -48,7 +48,7 @@ describe("characters are removed", () => {
expect(removed).toHaveLength(1);
expect(removed).toEqual([
{
character: "a",
value: "a",
index: 0,
},
]);
Expand All @@ -63,13 +63,13 @@ describe("characters have changed", () => {
expect(removed).toHaveLength(1);
expect(removed).toEqual([
{
character: "b",
value: "b",
index: 1,
},
]);
expect(added).toEqual([
{
character: "z",
value: "z",
index: 1,
},
]);
Expand All @@ -82,29 +82,29 @@ describe("characters have changed", () => {
expect(removed).toHaveLength(3);
expect(removed).toEqual([
{
character: "a",
value: "a",
index: 0,
},
{
character: "b",
value: "b",
index: 1,
},
{
character: "c",
value: "c",
index: 2,
},
]);
expect(added).toEqual([
{
character: "x",
value: "x",
index: 0,
},
{
character: "y",
value: "y",
index: 1,
},
{
character: "z",
value: "z",
index: 2,
},
]);
Expand All @@ -119,37 +119,37 @@ describe("characters are changed and added", () => {
expect(removed).toHaveLength(3);
expect(removed).toEqual([
{
character: "a",
value: "a",
index: 0,
},
{
character: "b",
value: "b",
index: 1,
},
{
character: "c",
value: "c",
index: 2,
},
]);
expect(added).toEqual([
{
character: "x",
value: "x",
index: 0,
},
{
character: "y",
value: "y",
index: 1,
},
{
character: "z",
value: "z",
index: 2,
},
{
character: "1",
value: "1",
index: 3,
},
{
character: "2",
value: "2",
index: 4,
},
]);
Expand Down
65 changes: 58 additions & 7 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,65 @@
import { DiffResult } from "./types";
import { fillStrings } from "./utils/fillStrings";
import getDiff from "./utils/getDiff";
import { DiffResult, Character } from "./types";
import deleteRef from "./utils/deleteRef";
import getAllParts from "./utils/getAllParts";
import toCharacters from "./utils/toCharacters";

const striff = (str1: string, str2: string): DiffResult => {
let [strArr1, strArr2] = fillStrings(str1, str2);
const getDiff = (partsArr: Character[][], arr1: Character[], arr2: Character[]) => {
let str2 = arr2.map((c: Character) => c.value).join("");
let matched = new Map<Symbol | null, string>();
let matchedIndicies: {[key: string]: number} = {};

partsArr.forEach((part: Character[]) => {
let partString = part.map((c) => c.value).join("");
let pattern = new RegExp(partString);

// If this little thing matches ANY part of the string, it's in.
let result = pattern.exec(str2);

// We found a match, so let's spread it into our 'matched' store.
let pastMatch = matchedIndicies[result?.index || ""];

// Give matched strings of longer length over all others.
if (result && (!pastMatch || partString.length > pastMatch)) {
matchedIndicies[result.index] = partString.length;

// Since this is matched, I can safely update the second
// string with symbol references.
let partIndex = 0;
for (let i = result.index; i < part.length + result.index; i++) {
arr2[i].ref = part[partIndex].ref;
partIndex++;
}

// Throw each of the matched characters into storage.
part.forEach((char) => matched.set(char.ref, char.value));
}
});

let hasNoMatchingRef = (char: Character) => !matched.get(char.ref);

return {
added: getDiff(strArr2, strArr1),
removed: getDiff(strArr1, strArr2),
// First string characters NOT in the "matched" set.
removed: arr1.filter(hasNoMatchingRef).map(deleteRef),

// Second string characters NOT in the "matched" set.
added: arr2.filter(hasNoMatchingRef).map(deleteRef),
};
};

const striff = (str1: string, str2: string): DiffResult => {
let strArr1 = toCharacters(str1, true);
let strArr2 = toCharacters(str2).map((char, index) => {
let str1Char = strArr1[index];

if (str1Char?.value === char.value) {
char.ref = str1Char.ref;
}

return char;
});

let parts = getAllParts(strArr1);
return getDiff(parts, strArr1, strArr2);
};

export default striff;
20 changes: 8 additions & 12 deletions src/types.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,12 @@
export interface Diff {
character: string;
index: number;
export interface Character {
index: number,
value: string,
ref: Symbol | null
}

export interface DiffResult {
added: Diff[];
removed: Diff[];
}
export type PrunedCharacter = Pick<Character, "index" | "value">;

export interface Character {
value: string | null,
accountedFor: boolean
export interface DiffResult {
added: PrunedCharacter[];
removed: PrunedCharacter[];
}

export type FilledString = (string | null)[];
22 changes: 22 additions & 0 deletions src/utils/deleteRef.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import deleteRef from "./deleteRef";
import toCharacters from "./toCharacters";

it("deletes refs from characters", () => {
let string = toCharacters("abc");
let result = string.map(deleteRef);

expect(result).toEqual([
{
index: 0,
value: "a",
},
{
index: 1,
value: "b",
},
{
index: 2,
value: "c",
},
]);
});
9 changes: 9 additions & 0 deletions src/utils/deleteRef.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { Character, PrunedCharacter } from "../types";

let deleteRef = (char: Partial<Character>) => {
delete char.ref;

return char as PrunedCharacter;
};

export default deleteRef;
Loading

0 comments on commit 974258f

Please sign in to comment.