Created
April 18, 2019 11:52
-
-
Save loonix/8576e960b96e6e9289820856106d5045 to your computer and use it in GitHub Desktop.
Find Best Match Typescript
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { TestBed, inject } from '@angular/core/testing'; | |
import { FindMatchService } from './find-match.service'; | |
describe('FindMatchService', () => { | |
beforeEach(() => { | |
TestBed.configureTestingModule({ | |
providers: [FindMatchService] | |
}); | |
}); | |
it('should be created', inject( | |
[FindMatchService], | |
(service: FindMatchService) => { | |
expect(service).toBeTruthy(); | |
} | |
)); | |
describe('findBestMatch', () => { | |
const service: FindMatchService = this.FindMatchService; | |
const findBestMatch = service.findBestMatch; | |
const badArgsErrorMsg = | |
'Bad arguments: First argument should be a string, second should be an array of strings'; | |
it('is a function', () => { | |
expect(typeof findBestMatch).toBe('function'); | |
}); | |
it('accepts a string and an array of strings and returns an object', () => { | |
const output = findBestMatch('one', ['two', 'three']); | |
expect(typeof output).toBe('object'); | |
}); | |
it('throws a \'Bad arguments\' error if second argument is not an array with at least one element', () => { | |
expect(() => { | |
findBestMatch('hello', 'something'); | |
}).toThrowError(badArgsErrorMsg); | |
expect(() => { | |
findBestMatch('hello', []); | |
}).toThrowError(badArgsErrorMsg); | |
}); | |
it('throws a \'Bad arguments\' error if second argument is not an array of strings', () => { | |
expect(() => { | |
findBestMatch('hello', [2, 'something']); | |
}).toThrowError(badArgsErrorMsg); | |
}); | |
it('assigns a similarity rating to each string passed in the array', () => { | |
const matches = findBestMatch('healed', [ | |
'mailed', | |
'edward', | |
'sealed', | |
'theatre' | |
]); | |
expect(matches.ratings).toEqual([ | |
{ target: 'mailed', rating: 0.4 }, | |
{ target: 'edward', rating: 0.2 }, | |
{ target: 'sealed', rating: 0.8 }, | |
{ target: 'theatre', rating: 0.36363636363636365 } | |
]); | |
}); | |
it('returns the best match and its similarity rating', () => { | |
const matches = findBestMatch('healed', [ | |
'mailed', | |
'edward', | |
'sealed', | |
'theatre' | |
]); | |
expect(matches.bestMatch).toEqual({ target: 'sealed', rating: 0.8 }); | |
}); | |
it('returns the index of best match from the target strings', () => { | |
const matches = findBestMatch('healed', [ | |
'mailed', | |
'edward', | |
'sealed', | |
'theatre' | |
]); | |
expect(matches.bestMatchIndex).toBe(2); | |
}); | |
}); | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { Injectable } from '@angular/core'; | |
@Injectable() | |
export class FindMatchService { | |
constructor() {} | |
findBestMatch(mainString, targetStrings): any { | |
if (!this.areArgsValid(mainString, targetStrings)) { | |
throw new Error( | |
'Bad arguments: First argument should be a string, second should be an array of strings' | |
); | |
} | |
const ratings = []; | |
let bestMatchIndex = 0; | |
for (let i = 0; i < targetStrings.length; i++) { | |
const currentTargetString = targetStrings[i]; | |
const currentRating = this.compareTwoStrings( | |
mainString, | |
currentTargetString | |
); | |
ratings.push({ target: currentTargetString, rating: currentRating }); | |
if (currentRating > ratings[bestMatchIndex].rating) { | |
bestMatchIndex = i; | |
} | |
} | |
const bestMatch = ratings[bestMatchIndex]; | |
// return { ratings, bestMatch, bestMatchIndex }; | |
return ratings[bestMatchIndex].target; | |
} | |
areArgsValid(mainString, targetStrings): any { | |
if (typeof mainString !== 'string') return false; | |
if (!Array.isArray(targetStrings)) return false; | |
if (!targetStrings.length) return false; | |
if (targetStrings.find(s => typeof s !== 'string')) return false; | |
return true; | |
} | |
compareTwoStrings(first, second): any { | |
first = first.replace(/\s+/g, ''); | |
second = second.replace(/\s+/g, ''); | |
if (!first.length && !second.length) return 1; // if both are empty strings | |
if (!first.length || !second.length) return 0; // if only one is empty string | |
if (first === second) return 1; // identical | |
if (first.length === 1 && second.length === 1) return 0; // both are 1-letter strings | |
if (first.length < 2 || second.length < 2) return 0; // if either is a 1-letter string | |
const firstBigrams = new Map(); | |
for (let i = 0; i < first.length - 1; i++) { | |
const bigram = first.substr(i, 2); | |
const count = firstBigrams.has(bigram) ? firstBigrams.get(bigram) + 1 : 1; | |
firstBigrams.set(bigram, count); | |
} | |
let intersectionSize = 0; | |
for (let i = 0; i < second.length - 1; i++) { | |
const bigram = second.substr(i, 2); | |
const count = firstBigrams.has(bigram) ? firstBigrams.get(bigram) : 0; | |
if (count > 0) { | |
firstBigrams.set(bigram, count - 1); | |
intersectionSize++; | |
} | |
} | |
return (2.0 * intersectionSize) / (first.length + second.length - 2); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment