String.js

  1. /**
  2. * @namespace String
  3. */
  4. /**
  5. * Calculate edit distance between string a and b
  6. *
  7. * @memberof String
  8. * @see {@link https://en.wikipedia.org/wiki/Edit_distance|Article on Wikipedia}
  9. * @param {string} a A
  10. * @param {string} b B
  11. * @returns {number} Distance
  12. */
  13. function editDistance(a, b) {
  14. let matrix = [], i, j;
  15. if(a.length === 0) { return b.length; }
  16. if(b.length === 0) { return a.length; }
  17. if(a === b) { return 0; }
  18. // increment along the first column of each row
  19. for(i = 0; i <= b.length; i++) {
  20. matrix[i] = [i];
  21. }
  22. // increment each column in the first row
  23. for(j = 0; j <= a.length; j++){
  24. matrix[0][j] = j;
  25. }
  26. // Fill in the rest of the matrix
  27. for(i = 1; i <= b.length; i++){
  28. for(j = 1; j <= a.length; j++){
  29. if(b.charAt(i-1) === a.charAt(j-1)){
  30. matrix[i][j] = matrix[i-1][j-1];
  31. } else {
  32. matrix[i][j] = Math.min(matrix[i-1][j-1] + 1, // substitution
  33. Math.min(matrix[i][j-1] + 1, // insertion
  34. matrix[i-1][j] + 1)); // deletion
  35. }
  36. }
  37. }
  38. return matrix[b.length][a.length];
  39. }
  40. /**
  41. * Get similarity ratio based on edit distance
  42. *
  43. * @memberof String
  44. * @see {@link String.editDistance}
  45. * @param {string} a A
  46. * @param {string} b B
  47. * @returns {number} Ratio
  48. */
  49. function getSimilarity(a, b) {
  50. const l = Math.max(a.length, b.length);
  51. return (l - String.editDistance(a, b)) / l;
  52. }
  53. /**
  54. * Checks is given value is String and is empty
  55. *
  56. * @memberof String
  57. * @param {string} sth Something to check
  58. * @returns {boolean} Verdict
  59. */
  60. function isEmpty (sth) {
  61. return typeof sth === 'string' && sth.length === 0;
  62. }
  63. /**
  64. * Checks is given value is String and is not empty
  65. *
  66. * @memberof String
  67. * @param {string} sth Something to check
  68. * @returns {boolean} Verdict
  69. */
  70. function isNotEmpty (sth) {
  71. return typeof sth === 'string' && sth.length > 0;
  72. }
  73. /**
  74. * Checks is given value isn't a String, or it is empty
  75. *
  76. * @memberof String
  77. * @param {string} sth Something to check
  78. * @returns {boolean} Verdict
  79. */
  80. function isInvalidOrEmpty (sth) {
  81. return typeof sth !== 'string' || sth.length === 0;
  82. }
  83. /**
  84. * Returns string with capitalised first letter
  85. *
  86. * @memberof String.prototype
  87. * @param {boolean} [lower=false] Flag if it should lower all letters first
  88. * @returns {string} New string
  89. */
  90. function capitaliseFirstLetter(lower) {
  91. let value = this.valueOf();
  92. if(lower) {
  93. value = value.toLowerCase();
  94. }
  95. return value.replace(/[a-z]/i, function(m) { return m.toUpperCase(); });
  96. }
  97. /**
  98. * Returns string with lower first letter
  99. *
  100. * @memberof String.prototype
  101. * @returns {string} New string
  102. */
  103. function lowerFirstLetter() {
  104. return this.valueOf().replace(/[a-z]/i, function(m) { return m.toLowerCase(); });
  105. }
  106. /**
  107. * Returns string with removed cases
  108. *
  109. * @memberof String.prototype
  110. * @returns {string} New string
  111. */
  112. function noCase() {
  113. let value = this.valueOf();
  114. // detect capitalized snake case
  115. if(/^[A-Z0-9_]+$/.test(value)) {
  116. return value.replace(/_/g, ' ').toLowerCase();
  117. }
  118. // clean kebab and snake case
  119. value = value.replace(/[-_]/g, ' ');
  120. // clean special characters
  121. value = value.replace(/[^a-z0-9 ]/gi, '');
  122. // clean pascal case
  123. value = value.lowerFirstLetter();
  124. // clean camel case
  125. value = value.replace(/([A-Za-z])([0-9])/g, function(m, m1, m2) { return m1 + ' ' + m2; });
  126. value = value.replace(/[A-Z][a-z]/g, function(m) { return ' ' + m.toLowerCase(); });
  127. value = value.replace(/([a-z0-9])([A-Z])/g, function(m, m1, m2) { return m1 + ' ' + m2; });
  128. // minimize white spaces
  129. value = value.trim().replace(/\s{2,}/g, ' ');
  130. return value;
  131. }
  132. /**
  133. * Returns string in camelCase
  134. *
  135. * @memberof String.prototype
  136. * @returns {string} String in camelCase
  137. */
  138. function toCamelCase() {
  139. let value = this.valueOf();
  140. // normalize
  141. value = value.noCase();
  142. // replace
  143. value = value.replace(/ [a-z0-9]/gi, function(m) { return m[1].toUpperCase(); });
  144. return value;
  145. }
  146. /**
  147. * Returns string in PascalCase
  148. *
  149. * @memberof String.prototype
  150. * @returns {string} String in PascalCase
  151. */
  152. function toPascalCase() {
  153. return this.toCamelCase().capitaliseFirstLetter(false);
  154. }
  155. /**
  156. * Returns string in kebab-case
  157. *
  158. * @memberof String.prototype
  159. * @returns {string} String in kebab-case
  160. */
  161. function toKebabCase() {
  162. let value = this.valueOf();
  163. // normalize
  164. value = value.noCase();
  165. // replace
  166. value = value.replace(/\s/g, '-');
  167. return value;
  168. }
  169. /**
  170. * Returns string in snake_case
  171. *
  172. * @memberof String.prototype
  173. * @param {boolean} [convertToUpperCase=false] Set this flag to convert to UpperCase
  174. * @returns {string} String in snake_case
  175. */
  176. function toSnakeCase(convertToUpperCase) {
  177. const toUpperCase = convertToUpperCase || false;
  178. let value = this.valueOf();
  179. // normalize
  180. value = value.noCase();
  181. // replace
  182. value = value.replace(/\s/g, '_');
  183. if(toUpperCase) return value.toUpperCase();
  184. return value;
  185. }
  186. /**
  187. * Returns checksum crc32
  188. *
  189. * @memberof String.prototype
  190. * @author joelpt
  191. * @author schnaader
  192. * @see {@link https://stackoverflow.com/a/3276730 | Stack Overflow Answer}
  193. * @returns {string} Checksum
  194. */
  195. function toChecksum() {
  196. let value, i, chk;
  197. value = this.valueOf();
  198. chk = 0x12345678;
  199. for (i = 0; i < value.length; i++) {
  200. chk += value.charCodeAt(i) * (i + 1);
  201. }
  202. return chk;
  203. }
  204. /**
  205. * Returns string in boolean
  206. *
  207. * @memberof String.prototype
  208. * @returns {boolean} True if string looks somehow like 'true'
  209. */
  210. function toBoolean() {
  211. return this.valueOf().toLowerCase() === 'true';
  212. }
  213. /**
  214. * Returns reversed string
  215. *
  216. * @memberof String.prototype
  217. * @returns {string} Reversed string
  218. */
  219. function reverse() {
  220. return this.valueOf().split('').reverse().join('');
  221. }
  222. /**
  223. * Check if string is like given query (you can use regexp notation)
  224. *
  225. * @memberof String.prototype
  226. * @param {string} query Query
  227. * @returns {boolean} Verdict
  228. */
  229. function isLike(query) {
  230. return new RegExp('^' + query + '$').test(this.valueOf());
  231. }
  232. /**
  233. * Polyfill for ECMAScript 2015 for String.prototype.includes
  234. *
  235. * @memberof String.prototype
  236. * @param {string} search Search for
  237. * @param {number} [start=0] Searching start position
  238. * @returns {boolean} Verdict
  239. */
  240. function includes(search, start) {
  241. const _start = typeof start !== 'number' ? 0 : start;
  242. if (_start + search.length > this.length) {
  243. return false;
  244. }
  245. return this.indexOf(search, _start) !== -1;
  246. }
  247. export default {
  248. static: {
  249. editDistance,
  250. getSimilarity,
  251. isEmpty,
  252. isNotEmpty,
  253. isInvalidOrEmpty
  254. },
  255. method: {
  256. capitaliseFirstLetter,
  257. lowerFirstLetter,
  258. noCase,
  259. toCamelCase,
  260. toPascalCase,
  261. toKebabCase,
  262. toSnakeCase,
  263. toChecksum,
  264. toBoolean,
  265. reverse,
  266. isLike,
  267. includes
  268. }
  269. };