4
\$\begingroup\$

Here is some working code for converting an input string to an output string:

const list = [
  { text: "i~&__", code: "\u14f0", name: 'Nasal German i with extra low tone' },
  { text: "i~&_", code: "\u14e2", name: 'Nasal German i with low tone' },
  { text: "i~&^^", code: "\u14ee", name: 'Nasal German i with extra high tone' },
  { text: "i~&^", code: "\u14ea", name: 'Nasal German i with high tone' },
  { text: "i~&", code: "\u14e6", name: 'Nasal German i' },
  { text: "(i~&__)", code: "\u14f1", name: 'Nasal German i with extra low tone and stress' },
  { text: "(i~&_)", code: "\u14e3", name: 'Nasal German i with low tone and stress' },
  { text: "(i~&^^)", code: "\u14ef", name: 'Nasal German i with extra high tone and stress' },
  { text: "(i~&^)", code: "\u14eb", name: 'Nasal German i with high tone and stress' },
  { text: "(i~&)", code: "\u14e7", name: 'Nasal German i with stress' },

  { text: "i~__", code: "\u14fa", name: 'German i with extra low tone' },
  { text: "i~_", code: "\u0434", name: 'German i with low tone' },
  { text: "i~^^", code: "\u14fe", name: 'German i with extra high tone' },
  { text: "i~^", code: "\u0438", name: 'German i with high tone' },
  { text: "i~", code: "\u0430", name: 'German i' },
  { text: "(i~__)", code: "\u14fb", name: 'German i with extra low tone and stress' },
  { text: "(i~_)", code: "\u0435", name: 'German i with low tone and stress' },
  { text: "(i~^^)", code: "\u14ff", name: 'German i with extra high tone and stress' },
  { text: "(i~^)", code: "\u0439", name: 'German i with high tone and stress' },
  { text: "(i~)", code: "\u0432", name: 'German i with stress' },

  { text: "i+&__", code: "\u1470", name: 'Nasal short i with extra low tone' },
  { text: "i+&_", code: "\u1462", name: 'Nasal short i with low tone' },
  { text: "i+&^^", code: "\u146e", name: 'Nasal short i with extra high tone' },
  { text: "i+&^", code: "\u146a", name: 'Nasal short i with high tone' },
  { text: "i+&", code: "\u1466", name: 'Nasal short i' },
  { text: "(i+&__)", code: "\u1471", name: 'Nasal short i with extra low tone and stress' },
  { text: "(i+&_)", code: "\u1463", name: 'Nasal short i with low tone and stress' },
  { text: "(i+&^^)", code: "\u146f", name: 'Nasal short i with extra high tone and stress' },
  { text: "(i+&^)", code: "\u146b", name: 'Nasal short i with high tone and stress' },
  { text: "(i+&)", code: "\u1467", name: 'Nasal short i with stress' },

  { text: "i+__", code: "\u147a", name: 'Short i with extra low tone' },
  { text: "i+_", code: "\u03d4", name: 'Short i with low tone' },
  { text: "i+^^", code: "\u147e", name: 'Short i with extra high tone' },
  { text: "i+^", code: "\u03d8", name: 'Short i with high tone' },
  { text: "i+", code: "\u03d0", name: 'Short i' },
  { text: "(i+__)", code: "\u147b", name: 'Short i with extra low tone and stress' },
  { text: "(i+_)", code: "\u03d5", name: 'Short i with low tone and stress' },
  { text: "(i+^^)", code: "\u147f", name: 'Short i with extra high tone and stress' },
  { text: "(i+^)", code: "\u03d9", name: 'Short i with high tone and stress' },
  { text: "(i+)", code: "\u03d2", name: 'Short i with stress' },

  { text: "i&__", code: "\u1530", name: 'Nasal i sound with extra low tone' },
  { text: "i&_", code: "\u1522", name: 'Nasal i sound with low tone' },
  { text: "i&^^", code: "\u152e", name: 'Nasal i sound with extra high tone' },
  { text: "i&^", code: "\u152a", name: 'Nasal i sound with high tone' },
  { text: "i&", code: "\u1526", name: 'Nasal i sound' },
  { text: "(i&__)", code: "\u1531", name: 'Nasal i sound with extra low tone and stress' },
  { text: "(i&_)", code: "\u1523", name: 'Nasal i sound with low tone and stress' },
  { text: "(i&^^)", code: "\u152f", name: 'Nasal i sound with extra high tone and stress' },
  { text: "(i&^)", code: "\u152b", name: 'Nasal i sound with high tone and stress' },
  { text: "(i&)", code: "\u1527", name: 'Nasal i sound with stress' },

  { text: "i__", code: "\u153a", name: 'I sound with extra low tone' },
  { text: "i_", code: "\u0474", name: 'I sound with low tone' },
  { text: "i^^", code: "\u153e", name: 'I sound with extra high tone' },
  { text: "i^", code: "\u0478", name: 'I sound with high tone' },
  { text: "i", code: "\u0470", name: 'I sound' },
  { text: "(i__)", code: "\u153b", name: 'I sound with extra low tone and stress' },
  { text: "(i_)", code: "\u0475", name: 'I sound with low tone and stress' },
  { text: "(i^^)", code: "\u153f", name: 'I sound with extra high tone and stress' },
  { text: "(i^)", code: "\u0479", name: 'I sound with high tone and stress' },
  { text: "(i)", code: "\u0472", name: 'I sound with stress' },

  { text: "e~&__", code: "\u1512", name: 'Nasal Danish oe with extra low tone' },
  { text: "e~&_", code: "\u1504", name: 'Nasal Danish oe with low tone' },
  { text: "e~&^^", code: "\u1500", name: 'Nasal Danish oe with extra high tone' },
  { text: "e~&^", code: "\u150c", name: 'Nasal Danish oe with high tone' },
  { text: "e~&", code: "\u1508", name: 'Nasal Danish oe' },
  { text: "(e~&__)", code: "\u1513", name: 'Nasal Danish oe with extra low tone and stress' },
  { text: "(e~&_)", code: "\u1505", name: 'Nasal Danish oe with low tone and stress' },
  { text: "(e~&^^)", code: "\u1501", name: 'Nasal Danish oe with extra high tone and stress' },
  { text: "(e~&^)", code: "\u150d", name: 'Nasal Danish oe with high tone and stress' },
  { text: "(e~&)", code: "\u1509", name: 'Nasal Danish oe with stress' },

  { text: "e~__", code: "\u1518", name: 'Danish oe with extra low tone' },
  { text: "e~_", code: "\u0456", name: 'Danish oe with low tone' },
  { text: "e~^^", code: "\u151c", name: 'Danish oe with extra high tone' },
  { text: "e~^", code: "\u045a", name: 'Danish oe with high tone' },
  { text: "e~", code: "\u0451", name: 'Danish oe' },
  { text: "(e~__)", code: "\u1519", name: 'Danish oe with extra low tone and stress' },
  { text: "(e~_)", code: "\u0457", name: 'Danish oe with low tone and stress' },
  { text: "(e~^^)", code: "\u151d", name: 'Danish oe with extra high tone and stress' },
  { text: "(e~^)", code: "\u045b", name: 'Danish oe with high tone and stress' },
  { text: "(e~)", code: "\u0453", name: 'Danish oe with stress' },

  { text: "e+&__", code: "\u1510", name: 'Nasal short e with extra low tone' },
  { text: "e+&_", code: "\u1502", name: 'Nasal short e with low tone' },
  { text: "e+&^^", code: "\u150e", name: 'Nasal short e with extra high tone' },
  { text: "e+&^", code: "\u150a", name: 'Nasal short e with high tone' },
  { text: "e+&", code: "\u1506", name: 'Nasal short e' },
  { text: "(e+&__)", code: "\u1511", name: 'Nasal short e with extra low tone and stress' },
  { text: "(e+&_)", code: "\u1503", name: 'Nasal short e with low tone and stress' },
  { text: "(e+&^^)", code: "\u150f", name: 'Nasal short e with extra high tone and stress' },
  { text: "(e+&^)", code: "\u150b", name: 'Nasal short e with high tone and stress' },
  { text: "(e+&)", code: "\u1507", name: 'Nasal short e with stress' },

  { text: "e+__", code: "\u151a", name: 'Short e with extra low tone' },
  { text: "e+_", code: "\u0454", name: 'Short e with low tone' },
  { text: "e+^^", code: "\u151e", name: 'Short e with extra high tone' },
  { text: "e+^", code: "\u0458", name: 'Short e with high tone' },
  { text: "e+", code: "\u0450", name: 'Short e' },
  { text: "(e+__)", code: "\u151b", name: 'Short e with extra low tone and stress' },
  { text: "(e+_)", code: "\u0455", name: 'Short e with low tone and stress' },
  { text: "(e+^^)", code: "\u151f", name: 'Short e with extra high tone and stress' },
  { text: "(e+^)", code: "\u0459", name: 'Short e with high tone and stress' },
  { text: "(e+)", code: "\u0452", name: 'Short e with stress' },

  { text: "e&__", code: "\u14d0", name: 'Nasal e sound with extra low tone' },
  { text: "e&_", code: "\u14c2", name: 'Nasal e sound with low tone' },
  { text: "e&^^", code: "\u14ce", name: 'Nasal e sound with extra high tone' },
  { text: "e&^", code: "\u14ca", name: 'Nasal e sound with high tone' },
  { text: "e&", code: "\u14c6", name: 'Nasal e sound' },
  { text: "(e&__)", code: "\u14d1", name: 'Nasal e sound with extra low tone and stress' },
  { text: "(e&_)", code: "\u14c3", name: 'Nasal e sound with low tone and stress' },
  { text: "(e&^^)", code: "\u14cf", name: 'Nasal e sound with extra high tone and stress' },
  { text: "(e&^)", code: "\u14cb", name: 'Nasal e sound with high tone and stress' },
  { text: "(e&)", code: "\u14c7", name: 'Nasal e sound with stress' },

  { text: "e__", code: "\u14da", name: 'E sound with extra low tone' },
  { text: "e_", code: "\u0414", name: 'E sound with low tone' },
  { text: "e^^", code: "\u14de", name: 'E sound with extra high tone' },
  { text: "e^", code: "\u0418", name: 'E sound with high tone' },
  { text: "e", code: "\u0410", name: 'E sound' },
  { text: "(e__)", code: "\u14db", name: 'E sound with extra low tone and stress' },
  { text: "(e_)", code: "\u0415", name: 'E sound with low tone and stress' },
  { text: "(e^^)", code: "\u14df", name: 'E sound with extra high tone and stress' },
  { text: "(e^)", code: "\u0419", name: 'E sound with high tone and stress' },
  { text: "(e)", code: "\u0412", name: 'E sound with stress' },

  { text: "a+~&__", code: "\u1492", name: 'Nasal Danish æ with extra low tone' },
  { text: "a+~&_", code: "\u148e", name: 'Nasal Danish æ with low tone' },
  { text: "a+~&^^", code: "\u148a", name: 'Nasal Danish æ with extra high tone' },
  { text: "a+~&^", code: "\u1486", name: 'Nasal Danish æ with high tone' },
  { text: "a+~&", code: "\u1482", name: 'Nasal Danish æ' },
  { text: "(a+~&__)", code: "\u1493", name: 'Nasal Danish æ with extra low tone and stress' },
  { text: "(a+~&_)", code: "\u148f", name: 'Nasal Danish æ with low tone and stress' },
  { text: "(a+~&^^)", code: "\u148b", name: 'Nasal Danish æ with extra high tone and stress' },
  { text: "(a+~&^)", code: "\u1487", name: 'Nasal Danish æ with high tone and stress' },
  { text: "(a+~&)", code: "\u1483", name: 'Nasal Danish æ with stress' },

  { text: "a+~__", code: "\u1498", name: 'Danish æ with extra low tone' },
  { text: "a+~_", code: "\u03b6", name: 'Danish æ with low tone' },
  { text: "a+~^^", code: "\u149c", name: 'Danish æ with extra high tone' },
  { text: "a+~^", code: "\u03ba", name: 'Danish æ with high tone' },
  { text: "a+~", code: "\u03b1", name: 'Danish æ' },
  { text: "(a+~__)", code: "\u1499", name: 'Danish æ with extra low tone and stress' },
  { text: "(a+~_)", code: "\u03b7", name: 'Danish æ with low tone and stress' },
  { text: "(a+~^^)", code: "\u149d", name: 'Danish æ with extra high tone and stress' },
  { text: "(a+~^)", code: "\u03bb", name: 'Danish æ with high tone and stress' },
  { text: "(a+~)", code: "\u03b3", name: 'Danish æ with stress' },

  { text: "a~&__", code: "\u1592", name: 'Nasal German e sound with extra low tone' },
  { text: "a~&_", code: "\u1584", name: 'Nasal German e sound with low tone' },
  { text: "a~&^^", code: "\u1580", name: 'Nasal German e sound with extra high tone' },
  { text: "a~&^", code: "\u158c", name: 'Nasal German e sound with high tone' },
  { text: "a~&", code: "\u1588", name: 'Nasal German e sound' },
  { text: "(a~&__)", code: "\u1593", name: 'Nasal German e sound with extra low tone and stress' },
  { text: "(a~&_)", code: "\u1585", name: 'Nasal German e sound with low tone and stress' },
  { text: "(a~&^^)", code: "\u1581", name: 'Nasal German e sound with extra high tone and stress' },
  { text: "(a~&^)", code: "\u158d", name: 'Nasal German e sound with high tone and stress' },
  { text: "(a~&)", code: "\u1589", name: 'Nasal German e sound with stress' },

  { text: "a~__", code: "\u1598", name: 'German e sound with extra low tone' },
  { text: "a~_", code: "\u0506", name: 'German e sound with low tone' },
  { text: "a~^^", code: "\u159c", name: 'German e sound with extra high tone' },
  { text: "a~^", code: "\u050a", name: 'German e sound with high tone' },
  { text: "a~", code: "\u0501", name: 'German e sound' },
  { text: "(a~__)", code: "\u1599", name: 'German e sound with extra low tone and stress' },
  { text: "(a~_)", code: "\u0507", name: 'German e sound with low tone and stress' },
  { text: "(a~^^)", code: "\u159d", name: 'German e sound with extra high tone and stress' },
  { text: "(a~^)", code: "\u050b", name: 'German e sound with high tone and stress' },
  { text: "(a~)", code: "\u0503", name: 'German e sound with stress' },

  { text: "a+&__", code: "\u1490", name: 'Nasal short a with extra low tone' },
  { text: "a+&_", code: "\u148c", name: 'Nasal short a with low tone' },
  { text: "a+&^^", code: "\u1488", name: 'Nasal short a with extra high tone' },
  { text: "a+&^", code: "\u1484", name: 'Nasal short a with high tone' },
  { text: "a+&", code: "\u1480", name: 'Nasal short a' },
  { text: "(a+&__)", code: "\u1491", name: 'Nasal short a with extra low tone and stress' },
  { text: "(a+&_)", code: "\u148d", name: 'Nasal short a with low tone and stress' },
  { text: "(a+&^^)", code: "\u1489", name: 'Nasal short a with extra high tone and stress' },
  { text: "(a+&^)", code: "\u1485", name: 'Nasal short a with high tone and stress' },
  { text: "(a+&)", code: "\u1481", name: 'Nasal short a with stress' },

  { text: "a+__", code: "\u149a", name: 'Short a with extra low tone' },
  { text: "a+_", code: "\u03b4", name: 'Short a with low tone' },
  { text: "a+^^", code: "\u149e", name: 'Short a with extra high tone' },
  { text: "a+^", code: "\u03b8", name: 'Short a with high tone' },
  { text: "a+", code: "\u03b0", name: 'Short a' },
  { text: "(a+__)", code: "\u149b", name: 'Short a with extra low tone and stress' },
  { text: "(a+_)", code: "\u03b5", name: 'Short a with low tone and stress' },
  { text: "(a+^^)", code: "\u149f", name: 'Short a with extra high tone and stress' },
  { text: "(a+^)", code: "\u03b9", name: 'Short a with high tone and stress' },
  { text: "(a+)", code: "\u03b2", name: 'Short a with stress' },

  { text: "a&__", code: "\u1590", name: 'Nasal a sound with extra low tone' },
  { text: "a&_", code: "\u1582", name: 'Nasal a sound with low tone' },
  { text: "a&^^", code: "\u158e", name: 'Nasal a sound with extra high tone' },
  { text: "a&^", code: "\u158a", name: 'Nasal a sound with high tone' },
  { text: "a&", code: "\u1586", name: 'Nasal a sound' },
  { text: "(a&__)", code: "\u1591", name: 'Nasal a sound with extra low tone and stress' },
  { text: "(a&_)", code: "\u1583", name: 'Nasal a sound with low tone and stress' },
  { text: "(a&^^)", code: "\u158f", name: 'Nasal a sound with extra high tone and stress' },
  { text: "(a&^)", code: "\u158b", name: 'Nasal a sound with high tone and stress' },
  { text: "(a&)", code: "\u1587", name: 'Nasal a sound with stress' },

  { text: "a__", code: "\u159a", name: 'A sound with extra low tone' },
  { text: "a_", code: "\u0504", name: 'A sound with low tone' },
  { text: "a^^", code: "\u159e", name: 'A sound with extra high tone' },
  { text: "a^", code: "\u0508", name: 'A sound with high tone' },
  { text: "a", code: "\u0500", name: 'A sound' },
  { text: "(a__)", code: "\u159b", name: 'A sound with extra low tone and stress' },
  { text: "(a_)", code: "\u0505", name: 'A sound with low tone and stress' },
  { text: "(a^^)", code: "\u159f", name: 'A sound with extra high tone and stress' },
  { text: "(a^)", code: "\u0509", name: 'A sound with high tone and stress' },
  { text: "(a)", code: "\u0502", name: 'A sound with stress' },

  { text: "o~&__", code: "\u1450", name: 'Nasal German o with extra low tone' },
  { text: "o~&_", code: "\u1442", name: 'Nasal German o with low tone' },
  { text: "o~&^^", code: "\u144e", name: 'Nasal German o with extra high tone' },
  { text: "o~&^", code: "\u144a", name: 'Nasal German o with high tone' },
  { text: "o~&", code: "\u1446", name: 'Nasal German o' },
  { text: "(o~&__)", code: "\u1451", name: 'Nasal German o with extra low tone and stress' },
  { text: "(o~&_)", code: "\u1443", name: 'Nasal German o with low tone and stress' },
  { text: "(o~&^^)", code: "\u144f", name: 'Nasal German o with extra high tone and stress' },
  { text: "(o~&^)", code: "\u144b", name: 'Nasal German o with high tone and stress' },
  { text: "(o~&)", code: "\u1447", name: 'Nasal German o with stress' },

  { text: "o~__", code: "\u145a", name: 'German o with extra low tone' },
  { text: "o~_", code: "\u0394", name: 'German o with low tone' },
  { text: "o~^^", code: "\u145e", name: 'German o with extra high tone' },
  { text: "o~^", code: "\u0398", name: 'German o with high tone' },
  { text: "o~", code: "\u0390", name: 'German o' },
  { text: "(o~__)", code: "\u145b", name: 'German o with extra low tone and stress' },
  { text: "(o~_)", code: "\u0395", name: 'German o with low tone and stress' },
  { text: "(o~^^)", code: "\u145f", name: 'German o with extra high tone and stress' },
  { text: "(o~^)", code: "\u0399", name: 'German o with high tone and stress' },
  { text: "(o~)", code: "\u0392", name: 'German o with stress' },

  { text: "o+&__", code: "\u1550", name: 'Nasal short o with extra low tone' },
  { text: "o+&_", code: "\u1542", name: 'Nasal short o with low tone' },
  { text: "o+&^^", code: "\u154e", name: 'Nasal short o with extra high tone' },
  { text: "o+&^", code: "\u154a", name: 'Nasal short o with high tone' },
  { text: "o+&", code: "\u1546", name: 'Nasal short o' },
  { text: "(o+&__)", code: "\u1551", name: 'Nasal short o with extra low tone and stress' },
  { text: "(o+&_)", code: "\u1543", name: 'Nasal short o with low tone and stress' },
  { text: "(o+&^^)", code: "\u154f", name: 'Nasal short o with extra high tone and stress' },
  { text: "(o+&^)", code: "\u154b", name: 'Nasal short o with high tone and stress' },
  { text: "(o+&)", code: "\u1547", name: 'Nasal short o stress' },

  { text: "o+__", code: "\u155a", name: 'Short o with extra low tone' },
  { text: "o+_", code: "\u04c4", name: 'Short o with low tone' },
  { text: "o+^^", code: "\u155e", name: 'Short o with extra high tone' },
  { text: "o+^", code: "\u04c8", name: 'Short o with high tone' },
  { text: "o+", code: "\u04c0", name: 'Short o' },
  { text: "(o+__)", code: "\u155b", name: 'Short o with extra low tone and stress' },
  { text: "(o+_)", code: "\u04c5", name: 'Short o with low tone and stress' },
  { text: "(o+^^)", code: "\u155f", name: 'Short o with extra high tone and stress' },
  { text: "(o+^)", code: "\u04c9", name: 'Short o with high tone and stress' },
  { text: "(o+)", code: "\u04c2", name: 'Short o stress' },

  { text: "o&__", code: "\u1570", name: 'Nasal o sound with extra low tone' },
  { text: "o&_", code: "\u1562", name: 'Nasal o sound with low tone' },
  { text: "o&^^", code: "\u156e", name: 'Nasal o sound with extra high tone' },
  { text: "o&^", code: "\u156a", name: 'Nasal o sound with high tone' },
  { text: "o&", code: "\u1566", name: 'Nasal o sound' },
  { text: "(o&__)", code: "\u1571", name: 'Nasal o sound with extra low tone and stress' },
  { text: "(o&_)", code: "\u1563", name: 'Nasal o sound with low tone and stress' },
  { text: "(o&^^)", code: "\u156f", name: 'Nasal o sound with extra high tone and stress' },
  { text: "(o&^)", code: "\u156b", name: 'Nasal o sound with high tone and stress' },
  { text: "(o&)", code: "\u1567", name: 'Nasal o sound with stress' },

  { text: "o__", code: "\u157a", name: 'O sound with extra low tone' },
  { text: "o_", code: "\u04e4", name: 'O sound with low tone' },
  { text: "o^^", code: "\u157e", name: 'O sound with extra high tone' },
  { text: "o^", code: "\u04e8", name: 'O sound with high tone' },
  { text: "o", code: "\u04e0", name: 'O sound' },
  { text: "(o__)", code: "\u157b", name: 'O sound with extra low tone and stress' },
  { text: "(o_)", code: "\u04e5", name: 'O sound with low tone and stress' },
  { text: "(o^^)", code: "\u157f", name: 'O sound with extra high tone and stress' },
  { text: "(o^)", code: "\u04e9", name: 'O sound with high tone and stress' },
  { text: "(o)", code: "\u04e2", name: 'O sound with stress' },

  { text: "u+&__", code: "\u15b0", name: 'Nasal short u with extra low tone' },
  { text: "u+&_", code: "\u15a2", name: 'Nasal short u with low tone' },
  { text: "u+&^^", code: "\u15ae", name: 'Nasal short u with extra high tone' },
  { text: "u+&^", code: "\u15aa", name: 'Nasal short u with high tone' },
  { text: "u+&", code: "\u15a6", name: 'Nasal short u' },
  { text: "(u+&__)", code: "\u15b1", name: 'Nasal short u with extra low tone and stress' },
  { text: "(u+&_)", code: "\u15a3", name: 'Nasal short u with low tone and stress' },
  { text: "(u+&^^)", code: "\u15af", name: 'Nasal short u with extra high tone and stress' },
  { text: "(u+&^)", code: "\u15ab", name: 'Nasal short u with high tone and stress' },
  { text: "(u+&)", code: "\u15a7", name: 'Nasal short u with stress' },

  { text: "u+__", code: "\u15ba", name: 'Short u with extra low tone' },
  { text: "u+_", code: "\u04a4", name: 'Short u with low tone' },
  { text: "u+^^", code: "\u15be", name: 'Short u with extra high tone' },
  { text: "u+^", code: "\u04a8", name: 'Short u with high tone' },
  { text: "u+", code: "\u04a0", name: 'Short u' },
  { text: "(u+__)", code: "\u15bb", name: 'Short u with extra low tone and stress' },
  { text: "(u+_)", code: "\u04a5", name: 'Short u with low tone and stress' },
  { text: "(u+^^)", code: "\u15bf", name: 'Short u with extra high tone and stress' },
  { text: "(u+^)", code: "\u04a9", name: 'Short u with high tone and stress' },
  { text: "(u+)", code: "\u04a2", name: 'Short u with stress' },

  { text: "u&__", code: "\u14b0", name: 'Nasal u sound with extra low tone' },
  { text: "u&_", code: "\u14a2", name: 'Nasal u sound with low tone' },
  { text: "u&^^", code: "\u14ae", name: 'Nasal u sound with extra high tone' },
  { text: "u&^", code: "\u14aa", name: 'Nasal u sound with high tone' },
  { text: "u&", code: "\u14a6", name: 'Nasal u sound' },
  { text: "(u&__)", code: "\u14b1", name: 'Nasal u sound with extra low tone and stress' },
  { text: "(u&_)", code: "\u14a3", name: 'Nasal u sound with low tone and stress' },
  { text: "(u&^^)", code: "\u14af", name: 'Nasal u sound with extra high tone and stress' },
  { text: "(u&^)", code: "\u14ab", name: 'Nasal u sound with high tone and stress' },
  { text: "(u&)", code: "\u14a7", name: 'Nasal u sound with stress' },

  { text: "u__", code: "\u14ba", name: 'U sound with extra low tone' },
  { text: "u_", code: "\u03f4", name: 'U sound with low tone' },
  { text: "u^^", code: "\u14be", name: 'U sound with extra high tone' },
  { text: "u^", code: "\u03f8", name: 'U sound with high tone' },
  { text: "u", code: "\u03f0", name: 'U sound' },
  { text: "(u__)", code: "\u14bb", name: 'U sound with extra low tone and stress' },
  { text: "(u_)", code: "\u03f5", name: 'U sound with low tone and stress' },
  { text: "(u^^)", code: "\u14bf", name: 'U sound with extra high tone and stress' },
  { text: "(u^)", code: "\u03f9", name: 'U sound with high tone and stress' },
  { text: "(u)", code: "\u03f2", name: 'U sound with stress' },

  { text: "m+", code: "\u0102", name: 'M sound with nasal quality' },
  { text: "m", code: "\u0100", name: 'M sound' },
  { text: "n+", code: "\u0142", name: 'Indian n sound' },
  { text: "n", code: "\u0140", name: 'N sound' },
  { text: "q", code: "\u0160", name: 'Ng sound' },
  { text: "g?", code: "\u0138", name: 'Implosive g sound' },
  { text: "g.", code: "\u135a", name: 'Stop g sound' },
  { text: "g@", code: "\u1357", name: 'Tense g sound' },
  { text: "g", code: "\u0130", name: 'G sound' },
  { text: "'", code: "\u01b0", name: 'Lack of sound, glottal stop' },
  { text: "\"", code: "\u01b2", name: 'Arabic voiced pharyngeal fricative' },
  { text: "d~", code: "\u00d1", name: 'Arabic d sound' },
  { text: "d!", code: "\u006c", name: 'Ejective d sound' },
  { text: "d?", code: "\u0068", name: 'Implosive d sound' },
  { text: "d*", code: "\u0064", name: 'Click d sound' },
  { text: "d+", code: "\u0062", name: 'Indian d sound' },
  { text: "d.", code: "\u123a", name: 'Stop d sound' },
  { text: "d@", code: "\u1237", name: 'Tense d sound' },
  { text: "d", code: "\u0060", name: 'D sound' },
  { text: "b?", code: "\u0048", name: 'Implosive b sound' },
  { text: "b!", code: "\u004c", name: 'Ejective b sound' },
  { text: "b.", code: "\u121a", name: 'Stop b sound' },
  { text: "b@", code: "\u1217", name: 'Tense b sound' },
  { text: "b", code: "\u0040", name: 'B sound' },
  { text: "p!", code: "\u0038", name: 'Ejective p sound' },
  { text: "p*", code: "\u0034", name: 'Click p sound' },
  { text: "p.", code: "\u120a", name: 'Stop p sound' },
  { text: "p@", code: "\u1207", name: 'Tense p sound' },
  { text: "p", code: "\u0030", name: 'P sound' },
  { text: "t+", code: "\u00d2", name: 'Indian t sound' },
  { text: "t!", code: "\u00dc", name: 'Ejective t sound' },
  { text: "t~", code: "\u00d1", name: 'Arabic t sound' },
  { text: "t*", code: "\u00d4", name: 'Click t sound' },
  { text: "t.", code: "\u129a", name: 'Stop t sound' },
  { text: "t@", code: "\u1297", name: 'Tense t sound' },
  { text: "t", code: "\u00d0", name: 'T sound' },
  { text: "k!", code: "\u0058", name: 'Ejective k sound' },
  { text: "k*", code: "\u0054", name: 'Click k sound' },
  { text: "k+", code: "\u0052", name: 'Arabic Q sound' },
  { text: "k+!", code: "\u0059", name: 'Arabic ejective Q sound' },
  { text: "k.", code: "\u122a", name: 'Stop k sound' },
  { text: "k@", code: "\u1227", name: 'Tense k sound' },
  { text: "k", code: "\u0050", name: 'K sound' },
  { text: "h+", code: "\u0122", name: 'Hebrew harsh h sound' },
  { text: "h~", code: "\u0121", name: 'Arabic h sound' },
  { text: "h", code: "\u0120", name: 'H sound' },
  { text: "j+", code: "\u0152", name: 'Indian j sound' },
  { text: "j", code: "\u0150", name: 'J sound' },
  { text: "s+", code: "\u0072", name: 'Navajo s sound' },
  { text: "s~", code: "\u0071", name: 'Arabic s sound' },
  { text: "s.", code: "\u124a", name: 'Stop s sound' },
  { text: "s@", code: "\u1247", name: 'Tense s sound' },
  { text: "s", code: "\u0070", name: 'S sound' },
  { text: "f+", code: "\u00c2", name: 'Labial f sound' },
  { text: "f", code: "\u00c0", name: 'f sound' },
  { text: "v+", code: "\u00f2", name: 'Labial v sound' },
  { text: "v~", code: "\u00f1", name: 'Other v sound' },
  { text: "v", code: "\u00f0", name: 'V sound' },
  { text: "z+", code: "\u0092", name: 'Zulu dl sound' },
  { text: "z", code: "\u0090", name: 'Z sound' },
  { text: "c+~", code: "\u00b2", name: 'Arabic voiced th sound' },
  { text: "c+", code: "\u00b0", name: 'Voiced th sound' },
  { text: "c", code: "\u0080", name: 'Unvoiced th sound' },
  { text: "l+", code: "\u0172", name: 'Indian l sound' },
  { text: "l*", code: "\u0174", name: 'Click l sound' },
  { text: "l~", code: "\u0171", name: 'Arabic l sound' },
  { text: "l", code: "\u0170", name: 'L sound' },
  { text: "r~", code: "\u01a2", name: 'French r sound' },
  { text: "r+", code: "\u00e2", name: 'Indian r sound' },
  { text: "r!", code: "\u00e0", name: 'Single rolling r sound' },
  { text: "r", code: "\u01a0", name: 'English r sound' },
  { text: "x+", code: "\u0192", name: 'Indian sh sound' },
  { text: "x.", code: "\u141a", name: 'Stop sh sound' },
  { text: "x@", code: "\u1417", name: 'Tense sh sound' },
  { text: "x", code: "\u0190", name: 'Sh sound' },
  { text: "w", code: "\u0110", name: 'W sound' },
  { text: "y", code: "\u0180", name: 'Y sound' },
  { text: "y+", code: "\u0182", name: 'Slight y sound' },
  { text: " ", code: "\u0020", name: 'Hanakana Space' },
  { text: ".", code: "\u0021", name: 'Hanakana Period' },
  { text: ",", code: "\u0024", name: 'Hanakana comma' },
  { text: "(", code: "\u0023", name: 'Hanakana opening parenthesis' },
  { text: ")", code: "\u002b", name: 'Hanakana closing parenthesis' },
  { text: "[", code: "\u0026", name: 'Hanakana opening bracket' },
  { text: "]", code: "\u0027", name: 'Hanakana closing bracket' },
  { text: "|", code: "\u0025", name: 'Hanakana pipe' },
  { text: "#", code: "\u0028", name: 'Hanakana at sign' },
  { text: "/", code: "\u0029", name: 'Hanakana forward slash' },
  { text: "\\", code: "\u002a", name: 'Hanakana backward slash' },
  { text: ":", code: "\u0022", name: 'Hanakana colon' }
]

const look = {}
list.forEach(({ text }) => {
  look[text] = '^' + text.replace(/[\(\)\*\!\?\^\+\_\.\[\]\|\\\/]/g, _ => `\\${_}`)
})

const form = text => {
  let remaining = text.replace(/([A-Z])/g, (_, $1) => `${$1.toLowerCase()}+`)
  let output = []
  a:
  while (remaining.length) {
    for (let node of list) {
      const pattern = look[node.text]
      const regex = new RegExp(pattern)
      const match = remaining.match(regex)
      if (match) {
        const val = node.code
        output.push(val)
        remaining = remaining.substr(match[0].length)
        continue a
      }
    }
    throw new Error(`${remaining}:${text}`)
  }
  return output.join('')
}

console.log(form('asdf'))
console.log(form('helloworld'))

Given I would like to specify the patterns in this list object, how can I perhaps compile them and/or optimize the main form function which converts input to output? Perhaps into a trie of some sort?

\$\endgroup\$
1
  • 1
    \$\begingroup\$ I changed the title so that it describes what the code does per site goals: "State what your code does in your title, not your main concerns about it.". Please check that I haven't misrepresented your code, and correct it if I have. \$\endgroup\$ Jul 1 '21 at 6:34
5
\$\begingroup\$

One way to simplify

As far as I can tell, each of your RegExps just looks at the start of your string for certain exact content - at that point you probably don't want match but startsWith, and you probably don't want to compile RegExps when you can just use the strings you're looking for directly

It seems to me like you can get rid of look entirely and rewrite your for loop like:

for (let node of list) {
    if (remaining.startsWith(node.text)) {
        output.push(node.code);
        remaining = remaining.substr(node.text.length);
        continue a;
    }
}

which to me seems clearer, and also means you don't need to worry about messy escaping of regexp metacharacters. It also seems to improve performance quite a bit, though I haven't tested that very thoroughly.

How about a trie?

As you mentioned in a comment, a very natural approach could be to use a trie. There are probably better-written better-perform trie implementations out there already, but a decently-written decently-performing needn't be any more complicated than

const trie_insert = (trie, key, value) => {
  if ( ! key ) {
    trie.value = value;
    return;
  }
  if ( trie[key[0]] === undefined ) {
    trie[key[0]] = {};
  }
  trie_insert(trie[key[0]], key.substr(1), value);
}


const trie_match = (trie, str) => {
  let current_trie = trie;
  
  let current_depth = 0;
  let last_value = undefined;
  let depth_of_last_match = undefined;
    
  while (current_trie && current_depth <= str.length) {
    if (current_trie.value !== undefined) {
      last_value = current_trie.value;
      depth_of_last_match = current_depth;
    }

    current_trie = current_trie[str[current_depth]];
    current_depth++;
  }
  
  if (last_value === undefined) {
    throw new Error(`No match for "${key}"`);
  }

  return {
    length: depth_of_last_match,
    value: last_value
  };
}

Then form could be as simple as:

const form = text => {
  let remaining = text.replace(/([A-Z])/g, (_, $1) => `${$1.toLowerCase()}+`);
  let output = [];
  
  while (remaining) {
    let match = trie_match(lookup, remaining);
    output.push(match.value);
    remaining = remaining.substr(match.length);
  }

  return output.join("");
}

This approach also has the added benefit of not caring about the order of list - it might affect how long it takes to construct the trie at first, but the correctness of the result shouldn't be affected - unlike the original approach or the startsWith approach, which could both misbehave if, say, the order of the t+ and t checks was accidentally switched.

In my limited testing this scales much better than the startsWith approach, as does the next one:

Another regexp alternative

As Toby Speight mentioned in a comment, it might be an option to use a RegExp - but it's possible to do it with just one rather than several. Depending on how well the runtime optimizes it, that could perform very well - or it might not. Still, it should be worth trying to see how it goes.

A simple way to do this could simply be to escape all the patterns much like in your original post, but instead of converting each of the patterns into its own RegExp, joining them into one - perhaps a bit like

const patterns = [];
const lookup = {};

list.forEach((node) => {
  patterns.push(node.text.replace(/[\(\)\*\!\?\^\+\_\.\[\]\|\\\/]/g, _ => `\\${_}`));
  lookup[node.text] = node.code;
});

const full_pattern = new RegExp("^(" + patterns.join("|") + ")");

const form = text => {
  let remaining = text.replace(/([A-Z])/g, (_, $1) => `${$1.toLowerCase()}+`);
  let output = [];
  
  while (remaining) {
    const match = remaining.match(full_pattern);
    if (match) {
      output.push(lookup[match[0]]);
      remaining = remaining.substr(match[0].length);
    } else {
      throw new Error(`${remaining}:${text}`);
    }
  }

  return output.join("");
}

This simple implementation does care about the order of list (i~&|i~|i and i|i~|i~& behave differently), making it slightly less robust than the trie, though it is arguably simpler which might make up for that. Performance-wise it seems very slightly slower than the trie, but there may be ways to improve the implementation of the regex approach and change that.

\$\endgroup\$
4
  • \$\begingroup\$ Can I get any more optimized, by creating a trie or something like that? That way you don't have to iterate through every item of the list for each chunk, potentially. \$\endgroup\$
    – Lance
    Jun 30 '21 at 23:37
  • 1
    \$\begingroup\$ @LancePollard Now that you mention it, a trie does seem like an appropriate tool for the job. Should definitely be worth trying, at least. \$\endgroup\$
    – Sara J
    Jul 1 '21 at 0:00
  • 1
    \$\begingroup\$ Alternative: create a big regexp formed by regex-quoting all the patterns and joining them with |, and use that to determine the maximal match. Then lookup the match in a hash-table we formed from the patterns. This assumes that your regexp compiler optimises well, of course, in which case the innards would look much like the trie. \$\endgroup\$ Jul 1 '21 at 6:39
  • \$\begingroup\$ @SaraJ can you show how to do that? \$\endgroup\$
    – Lance
    Jul 1 '21 at 7:08

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.