You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

136 lines
3.8 KiB

4 years ago
  1. (function() {
  2. 'use strict';
  3. var collator;
  4. try {
  5. collator = (typeof Intl !== "undefined" && typeof Intl.Collator !== "undefined") ? Intl.Collator("generic", { sensitivity: "base" }) : null;
  6. } catch (err){
  7. console.log("Collator could not be initialized and wouldn't be used");
  8. }
  9. // arrays to re-use
  10. var prevRow = [],
  11. str2Char = [];
  12. /**
  13. * Based on the algorithm at http://en.wikipedia.org/wiki/Levenshtein_distance.
  14. */
  15. var Levenshtein = {
  16. /**
  17. * Calculate levenshtein distance of the two strings.
  18. *
  19. * @param str1 String the first string.
  20. * @param str2 String the second string.
  21. * @param [options] Additional options.
  22. * @param [options.useCollator] Use `Intl.Collator` for locale-sensitive string comparison.
  23. * @return Integer the levenshtein distance (0 and above).
  24. */
  25. get: function(str1, str2, options) {
  26. var useCollator = (options && collator && options.useCollator);
  27. var str1Len = str1.length,
  28. str2Len = str2.length;
  29. // base cases
  30. if (str1Len === 0) return str2Len;
  31. if (str2Len === 0) return str1Len;
  32. // two rows
  33. var curCol, nextCol, i, j, tmp;
  34. // initialise previous row
  35. for (i=0; i<str2Len; ++i) {
  36. prevRow[i] = i;
  37. str2Char[i] = str2.charCodeAt(i);
  38. }
  39. prevRow[str2Len] = str2Len;
  40. var strCmp;
  41. if (useCollator) {
  42. // calculate current row distance from previous row using collator
  43. for (i = 0; i < str1Len; ++i) {
  44. nextCol = i + 1;
  45. for (j = 0; j < str2Len; ++j) {
  46. curCol = nextCol;
  47. // substution
  48. strCmp = 0 === collator.compare(str1.charAt(i), String.fromCharCode(str2Char[j]));
  49. nextCol = prevRow[j] + (strCmp ? 0 : 1);
  50. // insertion
  51. tmp = curCol + 1;
  52. if (nextCol > tmp) {
  53. nextCol = tmp;
  54. }
  55. // deletion
  56. tmp = prevRow[j + 1] + 1;
  57. if (nextCol > tmp) {
  58. nextCol = tmp;
  59. }
  60. // copy current col value into previous (in preparation for next iteration)
  61. prevRow[j] = curCol;
  62. }
  63. // copy last col value into previous (in preparation for next iteration)
  64. prevRow[j] = nextCol;
  65. }
  66. }
  67. else {
  68. // calculate current row distance from previous row without collator
  69. for (i = 0; i < str1Len; ++i) {
  70. nextCol = i + 1;
  71. for (j = 0; j < str2Len; ++j) {
  72. curCol = nextCol;
  73. // substution
  74. strCmp = str1.charCodeAt(i) === str2Char[j];
  75. nextCol = prevRow[j] + (strCmp ? 0 : 1);
  76. // insertion
  77. tmp = curCol + 1;
  78. if (nextCol > tmp) {
  79. nextCol = tmp;
  80. }
  81. // deletion
  82. tmp = prevRow[j + 1] + 1;
  83. if (nextCol > tmp) {
  84. nextCol = tmp;
  85. }
  86. // copy current col value into previous (in preparation for next iteration)
  87. prevRow[j] = curCol;
  88. }
  89. // copy last col value into previous (in preparation for next iteration)
  90. prevRow[j] = nextCol;
  91. }
  92. }
  93. return nextCol;
  94. }
  95. };
  96. // amd
  97. if (typeof define !== "undefined" && define !== null && define.amd) {
  98. define(function() {
  99. return Levenshtein;
  100. });
  101. }
  102. // commonjs
  103. else if (typeof module !== "undefined" && module !== null && typeof exports !== "undefined" && module.exports === exports) {
  104. module.exports = Levenshtein;
  105. }
  106. // web worker
  107. else if (typeof self !== "undefined" && typeof self.postMessage === 'function' && typeof self.importScripts === 'function') {
  108. self.Levenshtein = Levenshtein;
  109. }
  110. // browser main thread
  111. else if (typeof window !== "undefined" && window !== null) {
  112. window.Levenshtein = Levenshtein;
  113. }
  114. }());