Qualcosa è andato storto e gli elfi hanno fatto un piccolo pasticcio. Alcuni dei regali sono finiti in mezzo alla neve e i cartellini con i nomi si sono rovinati. Per fortuna la maggior parte delle lettere dei nomi sono visibili. Babbo Natale è convinto si possa ricostruire il nome intero a partire dai frammenti.

Il problema: Matching Gift Names 🎁

Il problema di oggi, il numero 7 del Dev Advent Calendar 🎅 è molto veloce. Ma richiede di usare le regular expressions. Onestamente faccio ancora fatica a maneggiare questo aspetto di JavaScript: trovo difficoltà anche con un problema semplice come questo.

Comincio con la soluzione:

import { default as names } from "../data/names.js";

export const matchedNames = (smudgedName) => {
  const nameWithRegex = smudgedName.replace(/#/gu, ".");
  const regex = new RegExp(`^${nameWithRegex}$`, "gu");

  return names.filter((name) => name.search(regex) > -1);
};

Allora, il primo passo, per me, è di provare varie combinazioni di regex e vedere qual è più adatta al problema. Per fare questo uso un sito molto ben fatto, regex101 e faccio un po’ di test manuali. Dopo aver individuato la regola (spero) giusta passo a creare la regular expression.

Ci sono due modi. Quello che finora ho usato più spesso è così:

const re = /hello/gu;

In genere è la soluzione migliore perché quella più efficiente. Prevede però di conoscere già in partenza l’espressione da usare. Non è questo il caso di oggi. Devo creare una regex diversa per ogni nome da controllare. Uso quindi:

const nameWithRegex = "hello";
const re = new RegExp(nameWithRegex, "gu");

Ovviamente questo codice funziona solamente se voglio cercare la parola hello. Non è questo il mio scopo.

Per prima cosa prendo la parola rovinata e sostituisco il carattere # con il carattere .. Come mai? Perché il punto, nelle regex, indica un singolo carattere qualsiasi. In questo modo posso trasformare h#ello nella stringa h.ello da usare direttamente come regex nel passaggio successivo. Quindi, scritto in codice:

const nameWithRegex = smudgedName.replace(/#/gu, ".");

Il passaggio successivo richiede osservare com’è la lista dei nomi. Poiché si tratta di un array ogni nome è a se stante. Posso quindi dare per scontato che il confronto avvenga tra stringhe complete. Aggiungo quindi due comandi:

  • ^ indica che il pattern da trovare è all’inizio della stringa
  • $ indica che dopo l’ultimo carattere del pattern non c’è più nulla.

In questo modo mi assicuro che patt. restituisca solamente patty e patti ma non patterson.

const regex = new RegExp(`^${nameWithRegex}$`, "gu");

Dopo aver trovato la regex da usare non resta che usarla effettivamente. Il problema è che non posso cavarmela con RegExp.prototype.test(). Il problema è che test() fa ricominciare la ricerca sempre dall’ultimi risultato trovato. Questo genera dei bug: credo che senza i test sul codice avrei avuto molte difficoltà a comprendere questo inghippo.

Di conseguenza ho deciso di usare String.prototype.search(): questo metodo fa partire la ricerca sempre dalla posizione 0, ed è esattamente quello che serve a me.

return names.filter((name) => name.search(regex) > -1);

Bene, questo è tutto per oggi.

Chi invece è curioso di leggere le puntate precedenti di questa serie di articoli sul 🎅 Dev Advent Calendar, può seguire questi link: