spanish-quizzer

An app to quiz you on Spanish vocabulary and verb conjugations
git clone https://git.ashermorgan.net/spanish-quizzer/
Log | Files | Refs | README

filters.js (17054B)


      1 /**
      2  * Create io-filters from an array of vocab filters.
      3  * @param {Array} rawFilters The array of filters.
      4  * @returns {Array} The io-filters.
      5  */
      6 function GetVocabFilters(rawFilters) {
      7     // Expand "All directions" filters
      8     let filters = [];
      9     for (let filter of rawFilters) {
     10         if (filter.direction === "Eng. ↔ Esp.") {
     11             filters.push({category:filter.category, type: filter.type, direction:"Eng. → Esp."});
     12             filters.push({category:filter.category, type: filter.type, direction:"Esp. → Eng."});
     13         }
     14         else {
     15             filters.push({category:filter.category, type: filter.type, direction:filter.direction});
     16         }
     17     }
     18 
     19     // Get category regex filter
     20     for (let filter of filters) {
     21         if (filter.category === "All Categories") {
     22             filter.category = ".*";
     23         }
     24     }
     25 
     26     // Get type regex filter
     27     for (let filter of filters) {
     28         switch (filter.type.toLowerCase()) {
     29             case "adjectives":
     30                 filter.type = "Adjective";
     31                 break;
     32             case "nouns":
     33                 filter.type = "Noun";
     34                 break;
     35             case "verbs":
     36                 filter.type = "Verb";
     37                 break;
     38             case "all types":
     39                 filter.type = ".*";
     40                 break;
     41             default:
     42                 throw `Unrecognized filter: ${type}.`;
     43         }
     44     }
     45 
     46     // Create io-filters
     47     let ioFilters = [];   // Format: [{outputIndex:0, inputIndex:0, filters:[{index:0, value:"regex"}]}]
     48     for (let filter of filters) {
     49         // Create filter
     50         if (filter.direction.toLowerCase().startsWith("eng")) {
     51             ioFilters.push({outputIndex:0, inputIndex:1, filters:[{index:2, value:filter.type}, {index:3, value:filter.category}]});
     52         }
     53         else {
     54             ioFilters.push({outputIndex:1, inputIndex:0, filters:[{index:2, value:filter.type}, {index:3, value:filter.category}]});
     55         }
     56     }
     57 
     58     // Return io-filters
     59     return ioFilters;
     60 }
     61 
     62 
     63 
     64 /**
     65  * Create io-filters from an array of verb filters.
     66  * @param {Array} rawFilters The array of filters.
     67  * @returns {Array} The io-filters.
     68  */
     69 function GetVerbFilters(rawFilters) {
     70     // Expand "All Tenses" filters
     71     let filters = [];   // Format: [{tense:"specific tense", subject:"specific subject", type:"regex"}]
     72     for (let filter of rawFilters) {
     73         if (filter.tense.toLowerCase() === "all tenses") {
     74             filters.push({ tense: "present participles", type: filter.type, subject: filter.subject, direction: filter.direction });
     75             filters.push({ tense: "past participles", type: filter.type, subject: filter.subject, direction: filter.direction });
     76             filters.push({ tense: "present tense", type: filter.type, subject: filter.subject, direction: filter.direction });
     77             filters.push({ tense: "preterite tense", type: filter.type, subject: filter.subject, direction: filter.direction });
     78             filters.push({ tense: "imperfect tense", type: filter.type, subject: filter.subject, direction: filter.direction });
     79             filters.push({ tense: "conditional tense", type: filter.type, subject: filter.subject, direction: filter.direction });
     80             filters.push({ tense: "simple future tense", type: filter.type, subject: filter.subject, direction: filter.direction });
     81             filters.push({ tense: "present subjunctive tense", type: filter.type, subject: filter.subject, direction: filter.direction });
     82             filters.push({ tense: "imperfect subjunctive tense", type: filter.type, subject: filter.subject, direction: filter.direction });
     83         }
     84         else {
     85             filters.push({ tense: filter.tense.toLowerCase(), type: filter.type, subject: filter.subject, direction: filter.direction });
     86         }
     87     }
     88 
     89     // Expand "All Subjects" filters
     90     for (let filter of filters) {
     91         if (filter.subject.toLowerCase() === "all subjects" && !["present participles", "past participles"].includes(filter.tense)) {
     92             filter.subject = "yo";
     93             filters.push({ tense: filter.tense, type: filter.type, subject: "tú", direction: filter.direction });
     94             filters.push({ tense: filter.tense, type: filter.type, subject: "él", direction: filter.direction });
     95             filters.push({ tense: filter.tense, type: filter.type, subject: "nosotros", direction: filter.direction });
     96             filters.push({ tense: filter.tense, type: filter.type, subject: "ellos", direction: filter.direction });
     97         }
     98         else {
     99             filter.subject = filter.subject.toLowerCase();
    100         }
    101     }
    102 
    103     // Replace regularities with regex filters
    104     for (let filter of filters) {
    105         switch (filter.type.toLowerCase()) {
    106             case "regular":
    107                 filter.type = "Regular";
    108                 break;
    109             case "reflexive":
    110                 filter.type = "Reflexive";
    111                 break;
    112             case "irregular":
    113                 filter.type = "Irregular";
    114                 break;
    115             case "stem changing":
    116                 filter.type = "Stem Changing";
    117                 break;
    118             case "orthographic":
    119                 filter.type = "Orthographic";
    120                 break;
    121             case "nonregular":
    122                 filter.type = "Irregular|Stem Changing|Orthographic";
    123                 break;
    124             case "all types":
    125                 filter.type = ".*";
    126                 break;
    127             default:
    128                 throw `Unrecognized filter: ${filter.type}.`;
    129         }
    130     }
    131 
    132     // Create io-filters
    133     let ioFilters = [];   // Format: [{outputIndex:0, inputIndex:0, filters:[{index:0, value:"regex"}]}]
    134     for (let filter of filters) {
    135         // Get output index
    136         let outputIndex;
    137         if (filter.direction.toLowerCase().includes("eng")) {
    138             outputIndex = 0;
    139         }
    140         else if (filter.direction.toLowerCase().includes("esp")) {
    141             outputIndex = 1;
    142         }
    143 
    144         // Get input index and filter index
    145         let inputIndex;
    146         let filterIndex;
    147         switch (filter.tense) {
    148             case "present participles":
    149                 filterIndex = 2;
    150                 switch (filter.subject) {
    151                     case "type":
    152                         inputIndex = filterIndex;
    153                         break;
    154                     default:
    155                         inputIndex = 3;
    156                         break;
    157                 }
    158                 break;
    159             case "past participles":
    160                 filterIndex = 4;
    161                 switch (filter.subject) {
    162                     case "type":
    163                         inputIndex = filterIndex;
    164                         break;
    165                     default:
    166                         inputIndex = 5;
    167                         break;
    168                 }
    169                 break;
    170             case "present tense":
    171                 filterIndex = 6;
    172                 switch (filter.subject) {
    173                     case "type":
    174                         inputIndex = filterIndex;
    175                         break;
    176                     case "yo":
    177                         inputIndex = 7;
    178                         break;
    179                     case "tú":
    180                         inputIndex = 8;
    181                         break;
    182                     case "él":
    183                         inputIndex = 9;
    184                         break;
    185                     case "nosotros":
    186                         inputIndex = 10;
    187                         break;
    188                     case "ellos":
    189                         inputIndex = 11;
    190                         break;
    191                     default:
    192                         throw `Unrecognized subject: ${filter.subject}.`;
    193                 }
    194                 break;
    195             case "preterite tense":
    196                 filterIndex = 12;
    197                 switch (filter.subject) {
    198                     case "type":
    199                         inputIndex = filterIndex;
    200                         break;
    201                     case "yo":
    202                         inputIndex = 13;
    203                         break;
    204                     case "tú":
    205                         inputIndex = 14;
    206                         break;
    207                     case "él":
    208                         inputIndex = 15;
    209                         break;
    210                     case "nosotros":
    211                         inputIndex = 16;
    212                         break;
    213                     case "ellos":
    214                         inputIndex = 17;
    215                         break;
    216                     default:
    217                         throw `Unrecognized subject: ${filter.subject}.`;
    218                 }
    219                 break;
    220             case "imperfect tense":
    221                 filterIndex = 18;
    222                 switch (filter.subject) {
    223                     case "type":
    224                         inputIndex = filterIndex;
    225                         break;
    226                     case "yo":
    227                         inputIndex = 19;
    228                         break;
    229                     case "tú":
    230                         inputIndex = 20;
    231                         break;
    232                     case "él":
    233                         inputIndex = 21;
    234                         break;
    235                     case "nosotros":
    236                         inputIndex = 22;
    237                         break;
    238                     case "ellos":
    239                         inputIndex = 23;
    240                         break;
    241                     default:
    242                         throw `Unrecognized subject: ${filter.subject}.`;
    243                 }
    244                 break;
    245             case "conditional tense":
    246                 filterIndex = 24;
    247                 switch (filter.subject) {
    248                     case "type":
    249                         inputIndex = filterIndex;
    250                         break;
    251                     case "yo":
    252                         inputIndex = 25;
    253                         break;
    254                     case "tú":
    255                         inputIndex = 26;
    256                         break;
    257                     case "él":
    258                         inputIndex = 27;
    259                         break;
    260                     case "nosotros":
    261                         inputIndex = 28;
    262                         break;
    263                     case "ellos":
    264                         inputIndex = 29;
    265                         break;
    266                     default:
    267                         throw `Unrecognized subject: ${filter.subject}.`;
    268                 }
    269                 break;
    270             case "simple future tense":
    271                 filterIndex = 30;
    272                 switch (filter.subject) {
    273                     case "type":
    274                         inputIndex = filterIndex;
    275                         break;
    276                     case "yo":
    277                         inputIndex = 31;
    278                         break;
    279                     case "tú":
    280                         inputIndex = 32;
    281                         break;
    282                     case "él":
    283                         inputIndex = 33;
    284                         break;
    285                     case "nosotros":
    286                         inputIndex = 34;
    287                         break;
    288                     case "ellos":
    289                         inputIndex = 35;
    290                         break;
    291                     default:
    292                         throw `Unrecognized subject: ${filter.subject}.`;
    293                 }
    294                 break;
    295             case "present subjunctive tense":
    296                 filterIndex = 36;
    297                 switch (filter.subject) {
    298                     case "type":
    299                         inputIndex = filterIndex;
    300                         break;
    301                     case "yo":
    302                         inputIndex = 37;
    303                         break;
    304                     case "tú":
    305                         inputIndex = 38;
    306                         break;
    307                     case "él":
    308                         inputIndex = 39;
    309                         break;
    310                     case "nosotros":
    311                         inputIndex = 40;
    312                         break;
    313                     case "ellos":
    314                         inputIndex = 41;
    315                         break;
    316                     default:
    317                         throw `Unrecognized subject: ${filter.subject}.`;
    318                 }
    319                 break;
    320             case "imperfect subjunctive tense":
    321                 filterIndex = 42;
    322                 switch (filter.subject) {
    323                     case "type":
    324                         inputIndex = filterIndex;
    325                         break;
    326                     case "yo":
    327                         inputIndex = 43;
    328                         break;
    329                     case "tú":
    330                         inputIndex = 44;
    331                         break;
    332                     case "él":
    333                         inputIndex = 45;
    334                         break;
    335                     case "nosotros":
    336                         inputIndex = 46;
    337                         break;
    338                     case "ellos":
    339                         inputIndex = 47;
    340                         break;
    341                     default:
    342                         throw `Unrecognized subject: ${filter.subject}.`;
    343                 }
    344                 break;
    345             default:
    346                 throw `Unrecognized tense: ${filter.tense}.`;
    347         }
    348 
    349         // Create filter
    350         if (filter.direction.toLowerCase().startsWith("conj")) {
    351             // Swap input and output
    352             ioFilters.push({outputIndex:inputIndex, inputIndex:outputIndex, filters:[{index:filterIndex, value:filter.type}]})
    353         }
    354         else {
    355             ioFilters.push({outputIndex:outputIndex, inputIndex:inputIndex, filters:[{index:filterIndex, value:filter.type}]})
    356         }
    357     }
    358 
    359     // Return io-filters
    360     return ioFilters;
    361 }
    362 
    363 
    364 
    365 /**
    366  * Creates an array of prompts from an array of io-filters.
    367  * @param {Object} terms The terms to filter.
    368  * @param {Array} filters The io-filters.
    369  * @returns {Array} The prompts.
    370  */
    371 function ApplyFilters(terms, filters, options={}) {
    372     // Set options
    373     if (!"multiplePrompts" in options) options.multiplePrompts = "Show together";
    374     if (!"removeDuplicates" in options) options.removeDuplicates = false;
    375 
    376     // Filter terms
    377     let results = [];   // Format: [[<output label>, <output>, <input label>, <input>]]
    378     for (let filter of filters) {
    379         // Iterate over terms (minus headers)
    380         for (let term of terms.slice(1)) {
    381             // Check against filters
    382             let matchesFilter = true;
    383             for (let filterFilter of filter.filters) {
    384                 if (!term[filterFilter.index].match(filterFilter.value)) {
    385                     matchesFilter = false;
    386                 }
    387             }
    388 
    389             // Add prompt
    390             if (matchesFilter) {
    391                 results.push([terms[0][filter.outputIndex], term[filter.outputIndex], terms[0][filter.inputIndex], term[filter.inputIndex]]);
    392             }
    393         }
    394     }
    395 
    396     // Iterate over prompts to enforce multiplePrompts setting
    397     for (let result of results) {
    398         // Get array of prompt outputs
    399         let prompts = result[1].split(/\s*,\s*/);
    400 
    401         // Check if multiple outputs exist
    402         if (prompts.length > 1) {
    403             switch (options.multiplePrompts) {
    404                 case "Show one":
    405                     // Set current prompt's output to a random prompt
    406                     result[1] = prompts[Math.floor(Math.random() * (prompts.length - 1))]
    407                     break;
    408 
    409                 case "Show separately":
    410                     result[1] = prompts[0]; // Set current prompt's output to 1st prompt
    411                     for (let prompt of prompts.splice(1)) {
    412                         // Add seperate prompts for extra outputs
    413                         results.push([result[0], prompt, result[2], result[3]])
    414                     }
    415                     break;
    416 
    417                 case "Show together":
    418                 default:
    419                     // Do nothing
    420                     break;
    421             }
    422         }
    423     }
    424 
    425     // Remove duplicate prompts
    426     if (options.removeDuplicates) {
    427         results = results.filter((result, index) => {
    428             return index === results.findIndex(obj => {
    429                 try {
    430                     return obj[0] === result[0] && obj[1] === result[1] && obj[2] === result[2] && obj[3] === result[3];
    431                 }
    432                 catch {
    433                     return false;
    434                 }
    435             });
    436         });
    437     }
    438 
    439     // Return prompts
    440     return results;
    441 }
    442 
    443 
    444 
    445 /**
    446  * Shuffles an array of items.
    447  * @param {Array} items - The array.
    448  */
    449 function Shuffle(items) {
    450     // Initialize variables
    451     var currentIndex = items.length;
    452     var temp;
    453     var randomIndex;
    454 
    455     // While there are more elements to shuffle
    456     while (0 !== currentIndex) {
    457         // Pick a remaining element
    458         randomIndex = Math.floor(Math.random() * currentIndex);
    459         currentIndex--;
    460 
    461         // Swap the two elements
    462         temp = items[currentIndex];
    463         items[currentIndex] = items[randomIndex];
    464         items[randomIndex] = temp;
    465     }
    466 
    467     // Return shuffled items
    468     return items;
    469 }