DiffHelper.js 1.4 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455
  1. const MAX_LENGTH = 254
  2. function _calculateRatio(matches, length) {
  3. if (length) {
  4. const ratio = (2.0 * matches) / length
  5. const rounded = Math.floor(ratio * 100) / 100
  6. return rounded
  7. }
  8. return 1.0
  9. }
  10. /**
  11. * Ported from python's `difflib`:
  12. * https://github.com/python/cpython/blob/0415cf895f96ae3f896f1f25f0c030a820845e13/Lib/difflib.py#L622-L649
  13. *
  14. * Accepts two strings, `a` and `b`, and returns a float ratio
  15. * corresponding (approximatey) to the overlap between the strings.
  16. * Identical strings produce 1.0, completely different strings produce 0.0
  17. * */
  18. function stringSimilarity(a, b) {
  19. if (
  20. typeof a !== 'string' ||
  21. typeof b !== 'string' ||
  22. a.length > MAX_LENGTH ||
  23. b.length > MAX_LENGTH
  24. ) {
  25. throw new Error('Invalid input to quickMatchRatio')
  26. }
  27. // Count how many times each character occurs in `b`
  28. const fullBCount = {}
  29. b.split('').forEach(e => {
  30. fullBCount[e] = (fullBCount[e] || 0) + 1
  31. })
  32. // avail[x] is the number of times x appears in 'b' less the
  33. // number of times we've seen it in 'a' so far ... kinda
  34. const avail = {}
  35. let matches = 0
  36. a.split('').forEach(e => {
  37. let n = null
  38. if (Object.hasOwn(avail, e)) {
  39. n = avail[e]
  40. } else {
  41. n = fullBCount[e] || 0
  42. }
  43. avail[e] = n - 1
  44. if (n > 0) {
  45. matches = matches + 1
  46. }
  47. })
  48. return _calculateRatio(matches, a.length + b.length)
  49. }
  50. module.exports = {
  51. stringSimilarity,
  52. }