Skip to content

Commit

Permalink
add the diffCharacters function
Browse files Browse the repository at this point in the history
  • Loading branch information
OlexG committed May 11, 2022
1 parent 2dc4263 commit 199af8f
Show file tree
Hide file tree
Showing 4 changed files with 127 additions and 3 deletions.
78 changes: 76 additions & 2 deletions mod.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { diffCharacter } from "./types.ts";
import { IDiffCharacter } from "./types.ts";

export function longestCommonSubsequence(a: string, b: string): string {
const arr: {
Expand Down Expand Up @@ -48,7 +48,11 @@ export function longestCommonSubsequence(a: string, b: string): string {
}
}
if (a[i] === b[j]) {
if (i > 0 && j > 0 && arr[i - 1][j - 1].length + 1 > currentObject.length) {
if (
i > 0 &&
j > 0 &&
arr[i - 1][j - 1].length + 1 > currentObject.length
) {
currentObject.length = arr[i - 1][j - 1].length + 1;
currentObject.previous = [i - 1, j - 1];
currentObject.character = a[i];
Expand All @@ -71,4 +75,74 @@ export function longestCommonSubsequence(a: string, b: string): string {
currentObject = arr[currentObject.previous[0]][currentObject.previous[1]];
}
return result;
}

export function diffCharacters(oldString: string, newString: string): IDiffCharacter[] {
const commonSubsequence = longestCommonSubsequence(oldString, newString);
const result: IDiffCharacter[] = [];
let oldStringPointer = 0;
let newStringPointer = 0;
let commonSubsequencePointer = 0;
while (
oldStringPointer < oldString.length ||
newStringPointer < newString.length ||
commonSubsequencePointer < commonSubsequence.length
) {
if (
oldStringPointer < oldString.length &&
oldString[oldStringPointer] !==
commonSubsequence[commonSubsequencePointer]
) {
result.push({
character: oldString[oldStringPointer],
wasAdded: false,
wasRemoved: true,
});
oldStringPointer++;
} else if (
newStringPointer < newString.length &&
newString[newStringPointer] !==
commonSubsequence[commonSubsequencePointer]
) {
result.push({
character: newString[newStringPointer],
wasAdded: true,
wasRemoved: false,
});
newStringPointer++;
} else if (
oldStringPointer < oldString.length &&
newStringPointer < newString.length &&
oldString[oldStringPointer] ===
commonSubsequence[commonSubsequencePointer] &&
newString[newStringPointer] ===
commonSubsequence[commonSubsequencePointer]
) {
result.push({
character: commonSubsequence[commonSubsequencePointer],
wasAdded: false,
wasRemoved: false,
});
oldStringPointer++;
newStringPointer++;
commonSubsequencePointer++;
}
}
return result;
}


for (const character of diffCharacters("boopa", "boop beep boppy")) {
let finalString = "";
if (character.wasRemoved) {
// print red if removed without newline
finalString += `\x1b[31m${character.character}\x1b[0m`;
} else if (character.wasAdded) {
// print green if added
finalString += `\x1b[32m${character.character}\x1b[0m`;
} else {
// print white if unchanged
finalString += `\x1b[37m${character.character}\x1b[0m`;
}
console.log(finalString);
}
3 changes: 3 additions & 0 deletions mod_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,7 @@ import {longestCommonSubsequence} from "./mod.ts";
Deno.test("Longest Common Subsequence", () => {
assertEquals(longestCommonSubsequence("abc", "abc"), "abc");
assertEquals(longestCommonSubsequence("abgd", "adh"), "ad");
assertEquals(longestCommonSubsequence("xabcd", "xabcf"), "xabc");
assertEquals(longestCommonSubsequence("ABCBDAB", "BDCABA"), "BCBA");
assertEquals(longestCommonSubsequence("BACDCABAAAADDACADDBBDAABDCDBBBCCDACCCDBDD", "BDACCACCDCCBADACDDACABBDAAABCDBBAADBCCCCB"), "BACDCBAADDACABBDAABCDBBBCCCCB");
});
47 changes: 47 additions & 0 deletions readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# diff

> Deno library for usage of diff
> Based on the O(n * m) DP solution to the LCS problem
```typescript
import { diffCharacters } from "https://deno.land/x/diff/mod.ts";

diffCharacters("ABCBDAB", "BDCABA")
```

## API

*diffCharacters(oldString: string, newString: string)*
Compares two strings by character and returns a list `IDiffCharacter` objects (explained below)

*longestCommonSubsequence(a: string, b: string)*
Compares two strings by character and returns the [longest common subsequence](https://en.wikipedia.org/wiki/Longest_common_subsequence_problem)

## Types

*IDiffCharacter*
```typescript
{
character: string;
wasAdded: boolean;
wasRemoved: boolean;
}
```
The object contains the character and whether that character was removed, added, or neither. Here is example usage
```typescript
for (const character of diffCharacters("boopa", "boop beep boppy")) {
let finalString = "";
if (character.wasRemoved) {
// print red if removed without newline
finalString += `\x1b[31m${character.character}\x1b[0m`;
} else if (character.wasAdded) {
// print green if added
finalString += `\x1b[32m${character.character}\x1b[0m`;
} else {
// print white if unchanged
finalString += `\x1b[37m${character.character}\x1b[0m`;
}
console.log(finalString);
}
```

2 changes: 1 addition & 1 deletion types.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export type diffCharacter = {
export type IDiffCharacter = {
character: string;
wasAdded: boolean;
wasRemoved: boolean;
Expand Down

0 comments on commit 199af8f

Please sign in to comment.