numbers.mjs 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164
  1. import { splitUTF32Number } from '../convert.mjs';
  2. import { createUTF16EmojiRegexItem, createSequenceEmojiRegexItem, createSetEmojiRegexItem, createOptionalEmojiRegexItem } from './base.mjs';
  3. import { vs16Emoji } from '../data.mjs';
  4. function createEmojiRegexItemForNumbers(numbers) {
  5. const utf32 = [];
  6. const utf16 = [];
  7. numbers.sort((a, b) => a - b);
  8. let lastNumber;
  9. for (let i = 0; i < numbers.length; i++) {
  10. const number = numbers[i];
  11. if (number === lastNumber) {
  12. continue;
  13. }
  14. lastNumber = number;
  15. const split = splitUTF32Number(number);
  16. if (!split) {
  17. utf16.push(number);
  18. continue;
  19. }
  20. const [first, second] = split;
  21. const item = utf32.find((item2) => item2.first === first);
  22. if (item) {
  23. item.second.push(second);
  24. item.numbers.push(number);
  25. } else {
  26. utf32.push({
  27. first,
  28. second: [second],
  29. numbers: [number]
  30. });
  31. }
  32. }
  33. const results = [];
  34. if (utf16.length) {
  35. results.push(createUTF16EmojiRegexItem(utf16));
  36. }
  37. if (utf32.length) {
  38. const utf32Set = [];
  39. for (let i = 0; i < utf32.length; i++) {
  40. const item = utf32[i];
  41. const secondRegex = createUTF16EmojiRegexItem(item.second);
  42. const listItem = utf32Set.find(
  43. (item2) => item2.second.regex === secondRegex.regex
  44. );
  45. if (listItem) {
  46. listItem.first.push(item.first);
  47. listItem.numbers = [...listItem.numbers, ...item.numbers];
  48. } else {
  49. utf32Set.push({
  50. second: secondRegex,
  51. first: [item.first],
  52. numbers: [...item.numbers]
  53. });
  54. }
  55. }
  56. for (let i = 0; i < utf32Set.length; i++) {
  57. const item = utf32Set[i];
  58. const firstRegex = createUTF16EmojiRegexItem(item.first);
  59. const secondRegex = item.second;
  60. results.push(
  61. createSequenceEmojiRegexItem(
  62. [firstRegex, secondRegex],
  63. item.numbers
  64. )
  65. );
  66. }
  67. }
  68. return results.length === 1 ? results[0] : createSetEmojiRegexItem(results);
  69. }
  70. function createRegexForNumbersSequence(numbers, optionalVariations = true) {
  71. const items = [];
  72. for (let i = 0; i < numbers.length; i++) {
  73. const num = numbers[i];
  74. const split = splitUTF32Number(num);
  75. if (!split) {
  76. const item = createUTF16EmojiRegexItem([num]);
  77. if (optionalVariations && num === vs16Emoji) {
  78. items.push(createOptionalEmojiRegexItem(item));
  79. } else {
  80. items.push(item);
  81. }
  82. } else {
  83. items.push(createUTF16EmojiRegexItem([split[0]]));
  84. items.push(createUTF16EmojiRegexItem([split[1]]));
  85. }
  86. }
  87. if (items.length === 1) {
  88. return items[0];
  89. }
  90. const result = createSequenceEmojiRegexItem(items);
  91. if (numbers.length === 1 && items[0].type === "utf16") {
  92. result.numbers = [...numbers];
  93. }
  94. return result;
  95. }
  96. function optimiseNumbersSet(set) {
  97. const mandatoryMatches = {
  98. numbers: [],
  99. items: []
  100. };
  101. const optionalMatches = {
  102. numbers: [],
  103. items: []
  104. };
  105. const filteredItems = set.sets.filter((item) => {
  106. if (item.type === "optional") {
  107. const parentItem = item.item;
  108. if (parentItem.numbers) {
  109. optionalMatches.items.push(item);
  110. optionalMatches.numbers = optionalMatches.numbers.concat(
  111. parentItem.numbers
  112. );
  113. return false;
  114. }
  115. return true;
  116. }
  117. if (item.numbers) {
  118. mandatoryMatches.items.push(item);
  119. mandatoryMatches.numbers = mandatoryMatches.numbers.concat(
  120. item.numbers
  121. );
  122. return false;
  123. }
  124. return true;
  125. });
  126. if (mandatoryMatches.items.length + optionalMatches.items.length < 2) {
  127. return set;
  128. }
  129. const optionalNumbers = new Set(optionalMatches.numbers);
  130. let foundMatches = false;
  131. mandatoryMatches.numbers = mandatoryMatches.numbers.filter((number) => {
  132. if (optionalNumbers.has(number)) {
  133. foundMatches = true;
  134. return false;
  135. }
  136. return true;
  137. });
  138. if (mandatoryMatches.items.length) {
  139. if (!foundMatches && mandatoryMatches.items.length === 1) {
  140. filteredItems.push(mandatoryMatches.items[0]);
  141. } else if (mandatoryMatches.numbers.length) {
  142. filteredItems.push(
  143. createEmojiRegexItemForNumbers(mandatoryMatches.numbers)
  144. );
  145. }
  146. }
  147. switch (optionalMatches.items.length) {
  148. case 0:
  149. break;
  150. case 1:
  151. filteredItems.push(optionalMatches.items[0]);
  152. break;
  153. default:
  154. filteredItems.push(
  155. createOptionalEmojiRegexItem(
  156. createEmojiRegexItemForNumbers(optionalMatches.numbers)
  157. )
  158. );
  159. }
  160. return filteredItems.length === 1 ? filteredItems[0] : createSetEmojiRegexItem(filteredItems);
  161. }
  162. export { createEmojiRegexItemForNumbers, createRegexForNumbersSequence, optimiseNumbersSet };