Skip to content

Commit

Permalink
Fix issue occurring in strings with same character used multiple times.
Browse files Browse the repository at this point in the history
  • Loading branch information
alexmacarthur committed Mar 22, 2022
1 parent 742d3d4 commit daa3f12
Show file tree
Hide file tree
Showing 6 changed files with 50 additions and 14 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "striff",
"description": "Real simple string diffing.",
"author": "Alex MacArthur (https://macarthur.me)",
"version": "1.0.0",
"version": "1.0.1",
"main": "dist/index.umd.js",
"module": "dist/index.es.js",
"types": "dist/index.d.ts",
Expand Down
9 changes: 8 additions & 1 deletion src/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ describe("characters are added", () => {
const { added, removed } = striff("hi pal", "hi, pal");

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

Expand Down Expand Up @@ -53,6 +53,13 @@ describe("characters are removed", () => {
},
]);
});

it("Correctly diffs when the same character is used multiple times.", () => {
const { added, removed } = striff("wow", "ow");

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

describe("characters have changed", () => {
Expand Down
30 changes: 19 additions & 11 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,36 +1,44 @@
import { DiffResult, Character } from "./types";
import deleteRef from "./utils/deleteRef";
import fromCharacters from "./utils/fromCharacters";
import getAllParts from "./utils/getAllParts";
import toCharacters from "./utils/toCharacters";

const getDiff = (partsArr: Character[][], arr1: Character[], arr2: Character[]) => {
let str2 = arr2.map((c: Character) => c.value).join("");
let str2 = fromCharacters(arr2);
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 partString = fromCharacters(part);
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 || ""];
let resultIndex = result?.index;
let pastMatchLength = matchedIndicies[resultIndex ?? ""] || 0;

// Give matched strings of longer length over all others.
if (result && (!pastMatch || partString.length > pastMatch)) {
matchedIndicies[result.index] = partString.length;
if (result && (partString.length > pastMatchLength)) {
matchedIndicies[resultIndex as number] = 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++;
part.forEach((partItem, index) => {
arr2[(resultIndex as number) + index].ref = partItem.ref;
})

// At this point, it's possible that other parts
// already matched the string and had their items set to the
// `matched` store. We need to find those indicies and unset them.
for (let i = result.index; i < (result.index + pastMatchLength); i++) {
let refToReset = arr1[i]?.ref;
matched.delete(refToReset);
}

// Throw each of the matched characters into storage.
// These will always be from the FIRST string, so each
// of them will already have a filled `ref`.
part.forEach((char) => matched.set(char.ref, char.value));
}
});
Expand Down
14 changes: 14 additions & 0 deletions src/utils/fromCharacters.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { Character } from "../types";
import fromCharacters from "./fromCharacters";

it("converts characters to a string", () => {
let characters: Character[] = [
{ value: "b", index: 0, ref: null },
{ value: "y", index: 1, ref: null },
{ value: "e", index: 2, ref: null },
];

let result = fromCharacters(characters);

expect(result).toEqual("bye");
});
7 changes: 7 additions & 0 deletions src/utils/fromCharacters.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { Character } from "../types";

let fromCharacters = (characters: Character[]): string => {
return characters.map((c: Character) => c.value).join("");
};

export default fromCharacters;
2 changes: 1 addition & 1 deletion src/utils/toCharacters.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Character } from "../types";

const toCharacters = (str: string, setRef = false): Character[] => {
let toCharacters = (str: string, setRef = false): Character[] => {
return str.split("").map((char: string, index: number) => {
return { value: char, index, ref: setRef ? Symbol(char) : null };
});
Expand Down

0 comments on commit daa3f12

Please sign in to comment.