using System; using System.Collections.Generic; using System.Linq; using Developpez.Dotnet.Properties; using JetBrains.Annotations; namespace Developpez.Dotnet.Collections { public static partial class EnumerableExtensions { #region BinarySearch and related methods /// /// Effectue une recherche binaire sur une liste triée pour trouver un élément selon /// la valeur d'une de ses propriétés. La liste doit être triée selon cette propriété. /// /// Type des éléments de la collection /// Type de la clé de recherche /// Liste dans laquelle rechercher l'élément /// Fonction permettant d'obtenir la clé de recherche /// Valeur de la clé recherchée /// Le premier élément correspondant, s'il existe. Sinon, lève une InvalidOperationException. public static T BinarySearch( [NotNull] this IList list, [NotNull] Func keySelector, TKey key) where TKey : IComparable { T result; if (list.TryBinarySearch(keySelector, key, out result)) return result; throw new InvalidOperationException(ExceptionMessages.NoMatchingItemInList); } /// /// Effectue une recherche binaire sur une liste triée pour trouver un élément selon /// la valeur d'une de ses propriétés. La liste doit être triée selon cette propriété. /// Si aucun élément correspondant n'est trouvé, une valeur par défaut est renvoyée. /// /// Type des éléments de la collection /// Type de la clé de recherche /// Liste dans laquelle rechercher l'élément /// Fonction permettant d'obtenir la clé de recherche /// Valeur de la clé recherchée /// Le premier élément correspondant, s'il existe. Sinon, la valeur par défaut du type T. public static T BinarySearchOrDefault( [NotNull] this IList list, [NotNull] Func keySelector, TKey key) where TKey : IComparable { return list.BinarySearchOrDefault(keySelector, key, default(T)); } /// /// Effectue une recherche binaire sur une liste triée pour trouver un élément selon /// la valeur d'une de ses propriétés. La liste doit être triée selon cette propriété. /// Si aucun élément correspondant n'est trouvé, la valeur par défaut spécifiée est renvoyée. /// /// Type des éléments de la collection /// Type de la clé de recherche /// Liste dans laquelle rechercher l'élément /// Fonction permettant d'obtenir la clé de recherche /// Valeur de la clé recherchée /// La valeur par défaut à renvoyer si aucun élément correspondant n'est trouvé /// Le premier élément correspondant, s'il existe. Sinon, la valeur par défaut spécifiée. public static T BinarySearchOrDefault( [NotNull] this IList list, [NotNull] Func keySelector, TKey key, T defaultValue) where TKey : IComparable { T result; if (list.TryBinarySearch(keySelector, key, out result)) return result; return defaultValue; } /// /// Effectue une recherche binaire sur une liste triée pour tenter de trouver un élément /// selon la valeur d'une de ses propriétés. La liste doit être triée selon cette propriété. /// /// Type des éléments de la collection /// Type de la clé de recherche /// Liste dans laquelle rechercher l'élément /// Fonction permettant d'obtenir la clé de recherche /// Valeur de la clé recherchée /// Paramètre de sortie qui prend la valeur du premier élément trouvé. /// true si un élément correspondant est trouvé. Sinon, false. public static bool TryBinarySearch( [NotNull] this IList list, [NotNull] Func keySelector, TKey key, out T result) where TKey : IComparable { list.CheckArgumentNull("list"); keySelector.CheckArgumentNull("keySelector"); result = default(T); int min = 0; int max = list.Count; while (min < max) { int mid = (max + min) / 2; T midItem = list[mid]; TKey midKey = keySelector(midItem); int comp = midKey.CompareTo(key); if (comp < 0) { min = mid + 1; } else if (comp > 0) { max = mid - 1; } else { result = midItem; return true; } } if (min == max && keySelector(list[min]).CompareTo(key) == 0) { result = list[min]; return true; } return false; } #endregion #region QuickSort /// /// Trie la liste en utilisant l'algorithme Quicksort /// /// Type des éléments de la liste /// Liste à trier public static void QuickSort([NotNull] this IList list) { Comparison comparison = Comparer.Default.ToComparison(); QuickSort(list, comparison); } /// /// Trie la liste selon la clé spécifiée en utilisant l'algorithme Quicksort /// /// Type des éléments de la liste /// Type de la clé de tri /// Liste à trier /// Fonction qui renvoie la clé de tri public static void QuickSortBy( [NotNull] this IList list, [NotNull] Func keySelector) { keySelector.CheckArgumentNull("keySelector"); Comparison keyComparison = Comparer.Default.ToComparison(); Comparison comparison = (a, b) => keyComparison(keySelector(a), keySelector(b)); QuickSort(list, comparison); } /// /// Trie la liste en utilisant l'algorithme Quicksort, avec le comparateur spécifié /// /// Type des éléments de la liste /// Liste à trier /// Comparateur à utiliser public static void QuickSort( [NotNull] this IList list, [NotNull] IComparer comparer) { comparer.CheckArgumentNull("comparer"); QuickSort(list, comparer.ToComparison()); } /// /// Trie la liste en utilisant l'algorithme Quicksort, avec la fonction de comparaison spécifiée /// /// Type des éléments de la liste /// Liste à trier /// Fonction de comparaison à utiliser public static void QuickSort( [NotNull] this IList list, [NotNull] Comparison comparison) { list.CheckArgumentNull("list"); QuickSort(list, 0, list.Count - 1, comparison); } private static void QuickSort(IList list, int left, int right, Comparison comparison) { if (right > left) { int pivot = left; QuickSortPartition(list, left, right, ref pivot, comparison); QuickSort(list, left, pivot - 1, comparison); QuickSort(list, pivot + 1, right, comparison); } } private static void QuickSortPartition(IList list, int left, int right, ref int pivot, Comparison comparison) { T pivotValue = list[pivot]; list.Swap(pivot, right); int tmpIndex = left; for (int i = left; i < right; i++) { if (comparison(list[i], pivotValue) <= 0) { list.Swap(i, tmpIndex); tmpIndex++; } } list.Swap(tmpIndex, right); pivot = tmpIndex; } #endregion #region AddRange /// /// Ajoute plusieurs éléments à une collection /// /// Type des éléments de la collection /// Collection à laquelle ajouter des éléments /// Eléments à ajouter public static void AddRange( [NotNull] this ICollection collection, [NotNull] IEnumerable items) { collection.CheckArgumentNull("collection"); items.CheckArgumentNull("items"); foreach (var item in items) { collection.Add(item); } } #endregion #region RemoveRange /// /// Retire d'une liste le nombre d'éléments spécifié à partir de la position spécifiée /// /// Type des éléments de la liste /// Liste de laquelle on retire des éléments /// Position à partir de laquelle on retire des éléments /// Nombre d'éléments à retirer public static void RemoveRange( [NotNull] this IList list, int index, int count) { list.CheckArgumentNull("list"); index.CheckArgumentOutOfRange("index", 0, list.Count - 1); count.CheckArgumentOutOfRange("count", 0, list.Count - index); for (int i = 0; i < count; i++) { list.RemoveAt(index); } } #endregion #region InsertRange /// /// Insère les éléments spécifiés dans une liste à partir de la position spécifiée /// /// Type des éléments de la liste /// Liste dans laquelle on insère les éléments /// Position à partir de laquelle insérer les éléments /// Éléments à insérer public static void InsertRange( [NotNull] this IList list, int index, [NotNull] IEnumerable items) { list.CheckArgumentNull("list"); index.CheckArgumentOutOfRange("index", 0, list.Count); items.CheckArgumentNull("items"); foreach (var item in items) { list.Insert(index++, item); } } #endregion #region RemoveAll /// /// Retire d'une collection tous les éléments qui vérifient le prédicat spécifié /// /// Type des éléments de la liste /// Collection de laquelle on retire des éléments /// Prédicat qui indique si un élément doit être retiré /// Le nombre d'éléments retirés public static int RemoveAll( [NotNull] this ICollection collection, [NotNull] Func predicate) { collection.CheckArgumentNull("collection"); predicate.CheckArgumentNull("predicate"); var toRemove = collection.Where(predicate).ToList(); foreach (var item in toRemove) { collection.Remove(item); } return toRemove.Count; } #endregion #region Shuffle /// /// Mélange une liste. /// /// Type des éléments de la séquence /// La liste à mélanger /// Cette méthode utilise l'algorithme de Fisher–Yates (http://en.wikipedia.org/wiki/Fisher-Yates_shuffle) /// public static void Shuffle([NotNull] this IList list) { list.Shuffle(new Random()); } /// /// Mélange une liste en spécifiant le générateur de nombres aléatoires à utiliser. /// /// Type des éléments de la séquence /// La liste à mélanger /// Le générateur de nombres aléatoires à utiliser /// Cette méthode utilise l'algorithme de Fisher–Yates (http://en.wikipedia.org/wiki/Fisher-Yates_shuffle) /// public static void Shuffle( [NotNull] this IList list, [NotNull] Random rnd) { list.CheckArgumentNull("list"); rnd.CheckArgumentNull("rnd"); for (int i = list.Count - 1; i > 0; i--) { int swapIndex = rnd.Next(i + 1); list.Swap(i, swapIndex); } } #endregion #region Swap /// /// Permute deux éléments d'une liste /// /// Type des éléments de la liste /// La liste contenant les éléments à permuter /// L'index du premier élément /// L'index du second élément public static void Swap( [NotNull] this IList list, int index1, int index2) { list.CheckArgumentNull("list"); index1.CheckArgumentOutOfRange("index1", 0, list.Count - 1); index1.CheckArgumentOutOfRange("index2", 0, list.Count - 1); T tmp = list[index1]; list[index1] = list[index2]; list[index2] = tmp; } #endregion #region ToHashSet /// /// Crée un à partir d'un avec le comparateur par défaut. /// /// Type des éléments de source /// Séquence à partir de laquelle créer un /// Un qui contient les éléments de la séquence d'entrée. public static HashSet ToHashSet([NotNull] this IEnumerable source) { source.CheckArgumentNull("source"); return new HashSet(source); } /// /// Crée un à partir d'un avec le comparateur spécifié. /// /// Type des éléments de source /// Séquence à partir de laquelle créer un /// Comparateur à utiliser pour comparer les éléments. /// Un qui contient les éléments de la séquence d'entrée. public static HashSet ToHashSet([NotNull] this IEnumerable source, IEqualityComparer comparer) { source.CheckArgumentNull("source"); return new HashSet(source, comparer); } #endregion #region ToStack /// /// Crée une à partir d'un . /// /// Type des éléments de source /// Séquence à partir de laquelle créer une /// Une qui contient les éléments de la séquence d'entrée. public static Stack ToStack([NotNull] this IEnumerable source) { source.CheckArgumentNull("source"); return new Stack(source); } #endregion #region ToQueue /// /// Crée une à partir d'un . /// /// Type des éléments de source /// Séquence à partir de laquelle créer une /// Une qui contient les éléments de la séquence d'entrée. public static Queue ToQueue([NotNull] this IEnumerable source) { source.CheckArgumentNull("source"); return new Queue(source); } #endregion } }