Introduction aux Set en JavaScript

Les Set en JavaScript sont des collections d'éléments uniques introduites dans ES6. Contrairement aux tableaux, les Set garantissent l'unicité des valeurs et préservent l'ordre d'insertion. Cependant, comme les Map, les Set ne disposent pas d'une méthode sort() intégrée. Dans ce guide complet, nous allons explorer différentes techniques pour trier un Set en JavaScript, que ce soit des nombres, des chaînes de caractères, ou des objets complexes.

Méthode 1 : Convertir en tableau, trier, puis recréer un Set

La méthode la plus courante pour trier un Set consiste à le convertir en tableau, le trier, puis créer un nouveau Set à partir du tableau trié. Cette approche est simple et efficace.

// Créer un Set de nombres
const monSet = new Set([3, 1, 4, 1, 5, 9, 2, 6]);
console.log(monSet); // Set(7) { 3, 1, 4, 5, 9, 2, 6 }

// Trier le Set (croissant)
const setTrie = new Set([...monSet].sort((a, b) => a - b));
console.log(setTrie); // Set(7) { 1, 2, 3, 4, 5, 6, 9 }

// Trier le Set (décroissant)
const setTrieDesc = new Set([...monSet].sort((a, b) => b - a));
console.log(setTrieDesc); // Set(7) { 9, 6, 5, 4, 3, 2, 1 }

Explication

L'opérateur de décomposition [...monSet] convertit le Set en tableau. Nous utilisons ensuite la méthode sort() avec une fonction de comparaison pour trier les éléments, puis nous créons un nouveau Set à partir du tableau trié. Notez que les valeurs dupliquées sont automatiquement supprimées lors de la création du Set initial.

Trier un Set de chaînes de caractères

Pour trier un Set contenant des chaînes de caractères, nous utilisons localeCompare() pour un tri alphabétique correct qui prend en compte les accents et les caractères spéciaux.

// Set de chaînes de caractères
const fruits = new Set(['pomme', 'banane', 'cerise', 'datte', 'ananas']);

// Trier alphabétiquement (croissant)
const fruitsTries = new Set([...fruits].sort((a, b) => a.localeCompare(b)));
console.log(fruitsTries);
// Set(5) { 'ananas', 'banane', 'cerise', 'datte', 'pomme' }

// Trier alphabétiquement (décroissant)
const fruitsTriesDesc = new Set([...fruits].sort((a, b) => b.localeCompare(a)));
console.log(fruitsTriesDesc);
// Set(5) { 'pomme', 'datte', 'cerise', 'banane', 'ananas' }

Trier un Set d'objets

Lorsque vous travaillez avec un Set d'objets, vous devez spécifier la propriété par laquelle vous souhaitez trier. Voici comment procéder :

// Set d'objets
const personnes = new Set([
  { nom: 'Alice', age: 30 },
  { nom: 'Bob', age: 25 },
  { nom: 'Charlie', age: 35 },
  { nom: 'Diana', age: 28 }
]);

// Trier par âge (croissant)
const personnesTrieesParAge = new Set(
  [...personnes].sort((a, b) => a.age - b.age)
);
console.log(personnesTrieesParAge);
// Set(4) { { nom: 'Bob', age: 25 }, { nom: 'Diana', age: 28 }, ... }

// Trier par nom (alphabétique)
const personnesTrieesParNom = new Set(
  [...personnes].sort((a, b) => a.nom.localeCompare(b.nom))
);
console.log(personnesTrieesParNom);
// Set(4) { { nom: 'Alice', age: 30 }, { nom: 'Bob', age: 25 }, ... }

Créer une fonction réutilisable

Pour une meilleure organisation du code, vous pouvez créer une fonction utilitaire générique qui fonctionne avec différents types de données :

function trierSet(set, comparateur) {
  return new Set([...set].sort(comparateur));
}

// Exemples d'utilisation

// Pour les nombres
const nombres = new Set([3, 1, 4, 1, 5, 9, 2, 6]);
const nombresTries = trierSet(nombres, (a, b) => a - b);

// Pour les chaînes
const mots = new Set(['zèbre', 'abeille', 'chat', 'chien']);
const motsTries = trierSet(mots, (a, b) => a.localeCompare(b));

// Pour les objets
const produits = new Set([
  { nom: 'Laptop', prix: 999 },
  { nom: 'Souris', prix: 25 },
  { nom: 'Clavier', prix: 75 }
]);
const produitsTries = trierSet(produits, (a, b) => a.prix - b.prix);

Fonction avancée avec options

Voici une version plus avancée qui gère automatiquement les différents types de données :

function trierSetAvance(set, options = {}) {
  const { 
    cle = null,           // Propriété à utiliser pour les objets
    ordre = 'croissant'   // 'croissant' ou 'decroissant'
  } = options;

  const comparer = (a, b) => {
    const aVal = cle ? a[cle] : a;
    const bVal = cle ? b[cle] : b;

    // Pour les nombres
    if (typeof aVal === 'number' && typeof bVal === 'number') {
      return ordre === 'croissant' ? aVal - bVal : bVal - aVal;
    }

    // Pour les chaînes
    if (typeof aVal === 'string' && typeof bVal === 'string') {
      const resultat = aVal.localeCompare(bVal);
      return ordre === 'croissant' ? resultat : -resultat;
    }

    // Comparaison générique
    if (ordre === 'croissant') {
      return aVal < bVal ? -1 : aVal > bVal ? 1 : 0;
    } else {
      return aVal > bVal ? -1 : aVal < bVal ? 1 : 0;
    }
  };

  return new Set([...set].sort(comparer));
}

// Utilisation
const personnes = new Set([
  { nom: 'Alice', age: 30 },
  { nom: 'Bob', age: 25 }
]);

// Trier par âge croissant
const parAge = trierSetAvance(personnes, { cle: 'age', ordre: 'croissant' });

// Trier par nom décroissant
const parNom = trierSetAvance(personnes, { cle: 'nom', ordre: 'decroissant' });

Cas d'usage pratiques

Scénario 1 : Trier des IDs uniques

// Vous avez un Set d'IDs utilisateur et vous voulez les trier
const userIds = new Set([42, 15, 7, 23, 99, 1]);
const userIdsTries = new Set([...userIds].sort((a, b) => a - b));
console.log(userIdsTries); // Set(6) { 1, 7, 15, 23, 42, 99 }

Scénario 2 : Trier des tags uniques

// Trier des tags de manière alphabétique
const tags = new Set(['javascript', 'react', 'nodejs', 'typescript', 'vue']);
const tagsTries = new Set([...tags].sort((a, b) => a.localeCompare(b)));
console.log(tagsTries);
// Set(5) { 'javascript', 'nodejs', 'react', 'typescript', 'vue' }

Scénario 3 : Trier des produits par prix

const produits = new Set([
  { id: 1, nom: 'Laptop', prix: 999 },
  { id: 2, nom: 'Souris', prix: 25 },
  { id: 3, nom: 'Clavier', prix: 75 },
  { id: 4, nom: 'Écran', prix: 200 }
]);

// Trier du moins cher au plus cher
const produitsTries = new Set(
  [...produits].sort((a, b) => a.prix - b.prix)
);

// Afficher les produits triés
produitsTries.forEach(produit => {
  console.log(`${produit.nom}: ${produit.prix}€`);
});
// Souris: 25€
// Clavier: 75€
// Écran: 200€
// Laptop: 999€

Performance et considérations

Le tri d'un Set a une complexité temporelle de O(n log n) due à l'algorithme de tri. Voici quelques points à considérer :

  • Les Set préservent l'unicité : même si vous triez, les doublons ne réapparaîtront pas

  • La conversion en tableau et la création d'un nouveau Set ont un coût en mémoire

  • Pour de très grandes collections, considérez l'utilisation d'un tableau trié ou d'une structure de données spécialisée

  • Si vous devez trier fréquemment, envisagez de maintenir un tableau trié parallèle plutôt que de trier à chaque fois

Comparaison avec les tableaux

Il est important de comprendre les différences entre Set et Array pour choisir la bonne structure de données :

  • Set : Garantit l'unicité, pas de méthode sort() intégrée, meilleur pour vérifier l'existence d'un élément

  • Array : Permet les doublons, méthode sort() intégrée, meilleur pour les opérations de tri fréquentes

Astuce : Si vous avez besoin à la fois d'unicité et de tri fréquent, considérez d'utiliser un tableau avec une vérification d'unicité lors de l'ajout, ou maintenez un Set pour l'unicité et un tableau trié pour l'affichage.

Conclusion

Nous avons couvert les aspects essentiels du tri de Set en JavaScript. Bien que les Set n'aient pas de méthode sort() intégrée, il est facile de les trier en les convertissant en tableaux. La méthode la plus courante consiste à utiliser l'opérateur de décomposition [...set], trier le tableau, puis créer un nouveau Set. Cette approche fonctionne efficacement pour les nombres, les chaînes de caractères, et même les objets complexes.

N'oubliez pas que les Set sont optimisés pour garantir l'unicité et vérifier l'existence d'éléments. Si le tri est une opération très fréquente dans votre cas d'usage, évaluez si un tableau ne serait pas plus approprié. La pratique et l'expérimentation vous aideront à choisir la meilleure structure de données pour vos besoins spécifiques.