Skip to content

Commit

Permalink
first commit
Browse files Browse the repository at this point in the history
  • Loading branch information
el3um4s committed Jul 12, 2020
1 parent a21a240 commit 638e30a
Show file tree
Hide file tree
Showing 8 changed files with 553 additions and 0 deletions.
47 changes: 47 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,49 @@
# Damerau-Levenshtein

[![GitHub license](https://img.shields.io/github/license/el3um4s/Damerau-Levenshtein.svg)](https://github.com/el3um4s/Damerau-Levenshtein/blob/master/LICENSE)
[![GitHub tag](https://img.shields.io/github/tag/el3um4s/Damerau-Levenshtein.svg)](https://GitHub.com/el3um4s/Damerau-Levenshtein/tags/)
[![HitCount](http://hits.dwyl.com/el3um4s/Damerau-Levenshtein.svg)](http://hits.dwyl.com/el3um4s/Damerau-Levenshtein)

Calculate the Damerau–Levenshtein distance between strings.

### Usage

Call to "distance" functions outputs an integer, the calculated Damerau-Levenshtein distance between 2 strings given as parameters. If the result is 0, strings are identical. The higher the result, the less similar strings are.

```typescript
import { distance} as damerau from "./mod.ts";

const firstString: string = "Hello";
const secondString: string = "Hello World";

const distance = damerau.distance(firstString, secondString);

console.log(
`Damerau–Levenshtein distance between "${firstString}" and "${secondString}" is: ${distance} `,
);
```

There are 1 demo available. The first demo queries Wikipedia and print the first result

```
deno run .\demo.ts "Hello Wordl" "Hello world"
```

You can run the demo directly from the repository with the commands:

```
deno run https://raw.githubusercontent.com/el3um4s/Damerau-Levenshtein/master/demo.ts "Hello Wordl" "Hello world"
```

I was inspirated by [fabvalaaah](https://github.com/fabvalaaah)'s repository [damerau-levenshtein-js](https://github.com/fabvalaaah/damerau-levenshtein-js)

### API

* **function compareDistance(a: StringWithDistance, b: StringWithDistance): number** : _Compare distance between 2 words (format like StringWithDistance)._
* **function distance(a: string, b: string)** : _Get the Damerau-Levenshtein distance between 2 strings_
* **function distanceDamerau(string: string, compared: string): StringWithDistance** : _Return an object with string, compared string and distance beetween_
* **function distanceList(target: string, list: Array<string>): Array<StringWithDistance>** : _Return an arry of StringWithDistance with the distance from the compared string_
* **function minDistance(string: string, list: Array<string>): number** : _Get the minimum Damerau-Levenshtein distance between a string and an array of strings_
* **function sortByMinDistance(list: Array<StringWithDistance>): Array<StringWithDistance>** : _Return an arry of StringWithDistance sorted by min distance_
* **function sortWordByMinDistance(target: string, list: Array<string>): Array<StringWithDistance>** : _Return an arry of StringWithDistance sorted by min distance_
* **interface StringWithDistance** : _Interface for string, compared string and distance beetween_
12 changes: 12 additions & 0 deletions demo.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { distance } from "./mod.ts";

const firstString: string = Deno.args.length > 0 ? Deno.args[0] : "Hello";
const secondString: string = Deno.args.length > 1
? Deno.args[1]
: "Hello World";

const d = distance(firstString, secondString);

console.log(
`Damerau–Levenshtein distance between "${firstString}" and "${secondString}" is: ${d} `,
);
125 changes: 125 additions & 0 deletions mod.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
/** Interface for string, compared string and distance beetween */
export interface StringWithDistance {
string: string;
compared: string;
distance: number;
}

const initMatrix = (a: string, b: string): number[][] => {
let d: number[][] = [];

for (let i = 0; i <= a.length; i++) {
d[i] = [];
d[i][0] = i;
}
for (let j = 0; j <= b.length; j++) {
d[0][j] = j;
}

return d;
};

const damerau = (
i: number,
j: number,
a: string,
b: string,
d: number[][],
cost: number,
) => {
if (i > 1 && j > 1 && a[i - 1] === b[j - 2] && a[i - 2] === b[j - 1]) {
d[i][j] = Math.min.apply(null, [d[i][j], d[i - 2][j - 2] + cost]);
}
};

/** Get the Damerau-Levenshtein distance between 2 strings */
export function distance(a: string, b: string) {
let d: number[][] = initMatrix(a, b);
for (var i = 1; i <= a.length; i++) {
let cost: number;
for (let j = 1; j <= b.length; j++) {
if (a.charAt(i - 1) === b.charAt(j - 1)) {
cost = 0;
} else {
cost = 1;
}

d[i][j] = Math.min.apply(null, [
d[i - 1][j] + 1,
d[i][j - 1] + 1,
d[i - 1][j - 1] + cost,
]);

damerau(i, j, a, b, d, cost);
}
}
return d[a.length][b.length];
}/** Return an arry of StringWithDistance with the distance from the compared string*/

export function distanceList(
target: string,
list: Array<string>,
): Array<StringWithDistance> {
return list.map((string) => {
return distanceDamerau(target, string);
});
}/** Return an object with string, compared string and distance beetween */

export function distanceDamerau(
string: string,
compared: string,
): StringWithDistance {
return {
string: string,
compared: compared,
distance: distance(string, compared),
};
}

/** Compare distance between 2 words (format like StringWithDistance). */
export function compareDistance(
a: StringWithDistance,
b: StringWithDistance,
): number {
return a.distance > b.distance ? 1 : a.distance < b.distance ? -1 : 0;
}

/** Get the minimum Damerau-Levenshtein distance between a string and an array of strings*/
export function minDistance(
string: string,
list: Array<string>,
): number {
const arrayStrings: Array<StringWithDistance> = distanceList(string, list);
return arrayStrings.length === 0 ? string.length : arrayStrings.reduce(
(min, b) => Math.min(min, b.distance),
arrayStrings[0].distance,
);
}/** Return an arry of StringWithDistance sorted by min distance */

export function sortByMinDistance(
list: Array<StringWithDistance>,
): Array<StringWithDistance> {
return list.concat().sort(compareDistance);
}/** Return an arry of StringWithDistance sorted by min distance */

export function sortWordByMinDistance(
target: string,
list: Array<string>,
): Array<StringWithDistance> {
const listWithDistance: Array<StringWithDistance> = distanceList(
target,
list,
);
return sortByMinDistance(listWithDistance);
}// interface CompareStrings {
// firstString: string;
// secondString: string;
// target: string;
// }
//
// export function compareDistanceBetweenWords(obj: CompareStrings): number {
// return compareDistance(
// distanceDamerau(obj.firstString, obj.target),
// distanceDamerau(obj.secondString, obj.target),
// );
// }
50 changes: 50 additions & 0 deletions test/compareDistanceBetweenWordstest.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
//
// Copyright 2020 Samuele de Tomasi. All rights reserved. MIT license.

import {
assertStrictEquals,
} from "https://deno.land/std/testing/asserts.ts";

import {
compareDistanceBetweenWords,
} from "../mod.ts";

Deno.test("compareDistanceBetweenWords (hello)(Heello)(hello)", (): void => {
const a: string = "hello";
const b: string = "Heello";
const target: string = "hello";
const result = compareDistanceBetweenWords(
{ firstString: a, secondString: b, target: target },
);
assertStrictEquals(result, -1);
});

Deno.test("compareDistanceBetweenWords (Milan)(Milano)(Milano)", (): void => {
const a: string = "Milan";
const b: string = "Milano";
const target: string = "Milano";
const result = compareDistanceBetweenWords(
{ firstString: a, secondString: b, target: target },
);
assertStrictEquals(result, 1);
});

Deno.test("compareDistanceBetweenWords (Milano)(Milano)(Milano)", (): void => {
const a: string = "Milano";
const b: string = "Milano";
const target: string = "Milano";
const result = compareDistanceBetweenWords(
{ firstString: a, secondString: b, target: target },
);
assertStrictEquals(result, 0);
});

Deno.test("compareDistanceBetweenWords (hello world)(HELLO WORLD)(HELLO world)", (): void => {
const a: string = "hello world";
const b: string = "HELLO WORLD";
const target: string = "HELLO world";
const result = compareDistanceBetweenWords(
{ firstString: a, secondString: b, target: target },
);
assertStrictEquals(result, 0);
});
74 changes: 74 additions & 0 deletions test/compareDistance_test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
// Copyright 2020 Samuele de Tomasi. All rights reserved. MIT license.

import {
assertStrictEquals,
} from "https://deno.land/std/testing/asserts.ts";

import {
compareDistance,
StringWithDistance,
} from "../mod.ts";

Deno.test("compareDistance a > b", (): void => {
const a: StringWithDistance = {
string: "hello",
compared: "Hello",
distance: 1,
};

const b: StringWithDistance = {
string: "hello",
compared: "Heello",
distance: 2,
};
const result = compareDistance(a, b);
assertStrictEquals(result, -1);
});

Deno.test("compareDistance a < b", (): void => {
const a: StringWithDistance = {
string: "Milan",
compared: "Milano",
distance: 1,
};

const b: StringWithDistance = {
string: "Milano",
compared: "Milano",
distance: 0,
};
const result = compareDistance(a, b);
assertStrictEquals(result, 1);
});

Deno.test("compareDistance a = b (Milano)(Milano)", (): void => {
const a: StringWithDistance = {
string: "Milano",
compared: "Milano",
distance: 0,
};

const b: StringWithDistance = {
string: "Milano",
compared: "Milano",
distance: 0,
};
const result = compareDistance(a, b);
assertStrictEquals(result, 0);
});

Deno.test("compareDistance a = b (hello world)(HELLO WORLD)", (): void => {
const a: StringWithDistance = {
string: "hello world",
compared: "HELLO world",
distance: 5,
};

const b: StringWithDistance = {
string: "HELLO WORLD",
compared: "HELLO world",
distance: 5,
};
const result = compareDistance(a, b);
assertStrictEquals(result, 0);
});
Loading

0 comments on commit 638e30a

Please sign in to comment.