Introducere

În JavaScript, hărțile și seturile sunt ambele structuri de date care vă permit să stocați orice cantitate de orice tip de date. Fiecare este iterabil, făcând posibilă accesarea și manipularea datelor cu ușurință și fiecare vine cu propriul set de metode predefinite. Cu toate acestea, în cea mai mare parte, asemănările dintre ele se termină aici.

Vă puteți gândi la o hartă ca la o versiune super-putere a unui obiect tipic. Aceasta:

  1. Stochează perechi cheie-valoare.
  2. Este semi-ordonat. Iterația are loc în aceeași ordine în care au fost inserate valorile. Toate cheile dintr-o hartă sunt enumerabile (ceea ce nu este întotdeauna adevărat pentru un obiect).
  3. Nu poate conține chei duplicate, dar poate conține valori duplicate.
  4. Are o proprietate de dimensiune, așa că returnarea numărului de perechi cheie-valoare într-o hartă este mai simplă decât într-un obiect.

Un set, pe de altă parte, este asemănător cu o matrice în construcția sa, fiecare element existând ca o singură valoare, spre deosebire de o pereche cheie-valoare. Cu toate acestea, acesta:

  1. Nu poate conține valori duplicat.
  2. Nu are o ordine garantatăîn care elementele nu pot fi accesate prin index. Dar, ca și în cazul unei hărți, iterația va avea loc în ordinea în care au fost introduse valorile.
  3. În general, are o complexitate de timp mai bunăîn comparație cu o matrice, mai ales când vine vorba de seturi de date mari.

După cum puteți vedea, hărțile și seturile oferă câteva avantaje unice față de obiecte și matrice, făcându-le instrumente valoroase pentru dezvoltatori. În acest articol, vom explora câteva dintre utilizările mai puțin cunoscute ale acestor structuri de date.

Conectarea tipurilor de date complexe la valori

Hărțile pot fi folosite pentru a stoca tipuri de date complexe - cum ar fi obiecte și funcții - ca chei. Acest lucru este util în cazurile în care trebuie să asociați un tip de date complex cu o anumită valoare:

const coolNewMap = new Map();
const averageObj = { name: "John", age: 30 };
const middlingFunc = function() { console.log("This is mediocre."); }

coolNewMap.set(averageObj, "some value");
coolNewMap.set(middlingFunc, "another value");

console.log(coolNewMap.get(averageObj)); // "some value"
console.log(coolNewMap.get(middlingFunc)); // "another value"

Aici, creăm o nouă hartă și adăugăm un obiect și o funcție ca chei cu valori asociate. Apoi luăm valorile folosind metoda get, care returnează valoarea asociată cu o cheie specificată.

Aceasta este o caracteristică utilă atunci când trebuie să memorați în cache rezultatul unui calcul consumator de timp - de exemplu, o solicitare API sau o operație matematică complexă:

const evenCoolerNewMap = new Map();

function expensiveComputation(value) {
  if (evenCoolerNewMap.has(value)) {
    return evenCoolerNewMap.get(value);
  } else {
    // perform expensive computation
    const result = evenCoolerNewMap.set(value, result);
    return result;
  }
}

Mai sus, definim o altă hartă nouă pentru a stoca rezultatele calculului nostru. Funcția expensiveComputation verifică mai întâi dacă harta conține deja o valoare pentru intrarea dată. Dacă da, funcția returnează rezultatul stocat în cache. În caz contrar, efectuează operația și stochează rezultatul în harta noastră înainte de a-l returna.

Eliminarea valorilor duplicate dintr-o matrice

Ștergerea valorilor repetate dintr-o matrice poate fi oarecum dificilă, deoarece trebuie să implice o combinație de buclă, folosind indexOf, reducerea și/sau filtrarea datelor. Procesul poate deveni atât de dureros încât „Undercore a creat chiar și o metodă întreagă” pentru a face față. Din fericire, utilizând seturi, putem efectua aceeași operație cu relativă ușurință:

const wordsThatShouldExist = ['memoreek', 'hoag', 'furty', 'hoag'];
const arrayToSet = new Set(wordsThatShouldExist);
const uniqueArray = Array.from(arrayToSet);

console.log(uniqueArray); // ['memoreek', 'hoag', 'furty']

În acest exemplu, creăm o nouă matrice care conține niște valori duplicate. Apoi îl transformăm într-un set, eliminând toate elementele neunice. Apoi, convertim setul înapoi într-o matrice, lăsându-ne cu rezultatul nostru: o matrice de valori unice.

Implementarea algoritmilor eficienți

O căutare pe lățimea întâi este un algoritm folosit pentru a căuta o structură de date, cum ar fi un arbore sau un grafic, pentru o anumită valoare. Procesul trebuie să înceapă de la nodul rădăcină și să exploreze fiecare nod la nivelul actual de adâncime înainte de a traversa mai adânc.

const oakTree = {
  value: 'Branch',
  children: [
    {
      value: 'Twig 1',
      children: [
        { value: 'small acorn', children: [] },
        { value: 'leaf', children: [] }
      ]
    },
    {
      value: 'Twig 2',
      children: [
        { value: 'eensy-weensy acorn', children: [] },
        { value: 'a second, weirder leaf', children: [] }
      ]
    }
  ]
};

function breadthFirstSearch(tree, target) {
  const nodes = [tree]; // create array with root node

  const visited = new Set(); // create set to keep track of visited nodes

  for (let node of nodes) {
    if (node.value === target) { // check value of current node
      return node;
    }
    if (!visited.has(node)) { // check if set contains current node
      visited.add(node); // if not, add node to set
      nodes.push(...node.children); // add children of node to array
    }
  }
  return null;
}

// Searching for the node with value 'leaf':
const result = breadthFirstSearch(oakTree, 'leaf');
console.log(result); // { value: 'leaf', children: [] }

Aici definim o structură de date arborescentă simplă. Apoi folosim funcția noastră breadthFirstSearch, care ia un arbore și o valoare țintă și returnează fie nodul cu valoarea potrivită, fie null dacă valoarea nu este găsită. Funcția folosește un set pentru a ține evidența nodurilor vizitate, astfel încât să putem evita să căutăm același nod de două ori.

Un avantaj al utilizării unui set în acest fel este că oferă timpi de căutare mai rapidi. Metoda matricei .includes are o complexitate temporală liniară sau O(n), deoarece trebuie să parcurgă fiecare valoare a unui tablou pentru a-și face treaba. Din fericire, nu același lucru este valabil și pentru metoda set echivalent! Pentru același tip de căutare, .has poartă o complexitate de timp medie de O(1). Când aveți de-a face cu seturi de date mai mici, diferența aici poate fi neglijabilă, dar pentru cele mai mari, impactul este uriaș.

În cele din urmă, utilizarea unui set ne permite să scriem un cod mai curat, mai simplu. Dacă am fi folosit o matrice pentru a urmări nodurile deja căutate, ar fi trebuit să trecem prin buclă și să verificăm fiecare valoare. În schimb, am putut să ne condensăm logica în două linii simple de cod. Efectul combinat? Cod mai rapid și mai eficient, cu mai puține oportunități pentru erori.

În concluzie…

Hărțile și seturile sunt structuri de date super cool în JavaScript care oferă câteva avantaje unice față de obiecte și matrice. Folosind hărți și seturi, suntem capabili să stocăm și să manipulăm tipuri complexe de date, să eliminăm duplicatele din matrice, să memorăm în cache calcule costisitoare, să implementăm cu ușurință algoritmi mai eficienți și multe altele. La sfârșitul zilei, utilizările acestor structuri de date sunt mult mai extinse decât aș putea acoperi într-un singur articol. Te-aș încuraja să experimentezi cu ele în timpul tău, deoarece dezvoltarea unei mai bune înțelegeri a avantajelor și limitărilor lor nu poate decât să te ajute să devii mai eficient ca dezvoltator!

Referințe