using System; using System.Collections; using System.Collections.Generic; using System.Linq; using Developpez.Dotnet.Linq; using Developpez.Dotnet.Properties; namespace Developpez.Dotnet.Collections { public static partial class EnumerableExtensions { #region IsNullOrEmpty /// /// Indique si une collection est nulle ou vide /// /// Le type des éléments de la collection /// La collection à tester /// true si la collection est nulle ou vide, false sinon public static bool IsNullOrEmpty(this IEnumerable collection) { if (collection == null) return true; return !collection.Any(); } /// /// Indique si une collection est nulle ou vide /// /// La collection à tester /// true si la collection est nulle ou vide, false sinon public static bool IsNullOrEmpty(this IEnumerable collection) { if (collection == null) return true; var collection2 = collection as ICollection; if (collection2 != null) return collection2.Count == 0; var enumerator = collection.GetEnumerator(); return !enumerator.MoveNext(); } #endregion #region Zip // Contournement d'un bug dans Resharper: la directive #if entre le commentaire de doc et la // méthode provoque ces deux avertissements (mais le comportement du compilateur est correct) #pragma warning disable 1587 #pragma warning disable 1591 /// /// Fusionne 2 séquences selon la fonction de projection spécifiée, en faisant correspondre /// chaque élément de la première séquence à l'élément de même index dans la deuxième séquence. /// /// Type des éléments de la première séquence /// Type des éléments de la deuxième séquence /// Type des éléments de la séquence fusionnée /// première séquence /// deuxième séquence /// fonction de projection pour fusionner des éléments des 2 séquences /// Une séquence d'éléments fusionnée /// /// Si les 2 séquences ne sont pas de même longueur, les éléments surnuméraires de la séquence la plus longue sont ignorés. /// Cette méthode est absente de la version compilée pour .NET 4, car elle est déjà définie dans le framework 4.0 (Enumerable.Zip) /// // La méthode Zip a été ajoutée dans .NET 4.0 et ne sera donc plus nécessaire // Par contre elle est utilisée en interne et doit donc rester présente #if DOTNET4 internal #else public #endif static IEnumerable Zip(this IEnumerable first, IEnumerable second, Func selector) { first.CheckArgumentNull("first"); second.CheckArgumentNull("second"); selector.CheckArgumentNull("selector"); return first.ZipIterator(second, selector); } #pragma warning restore 1591 #pragma warning restore 1587 private static IEnumerable ZipIterator(this IEnumerable first, IEnumerable second, Func selector) { using (var enum1 = first.GetEnumerator()) using (var enum2 = second.GetEnumerator()) { while (enum1.MoveNext() && enum2.MoveNext()) { yield return selector(enum1.Current, enum2.Current); } } } #endregion #region Append, Prepend /// /// Ajoute un élément à la suite de la séquence spécifiée /// /// Type des éléments de la séquence /// Séquence d'origine /// Elément à ajouter /// La séquence d'origine suivie de l'élément spécifiée public static IEnumerable Append(this IEnumerable source, TSource item) { return source.Concat(new[] { item }); } /// /// Insère un élément au début de la séquence spécifiée /// /// Type des éléments de la séquence /// Séquence d'origine /// Elément à insérer /// La séquence d'origine précédée de l'élément spécifiée public static IEnumerable Prepend(this IEnumerable source, TSource item) { return new[] { item }.Concat(source); } #endregion #region IsOrdered and related methods /// /// Vérifie que la liste est triée, en utilisant le comparateur spécifié /// /// Le type des éléments de la liste /// La liste à vérifier /// Le comparateur à utiliser /// true si la liste est triée, false sinon public static bool IsOrdered(this IEnumerable list, IComparer comparer) { return list.IsOrdered(comparer.ToComparison()); } /// /// Vérifie que la liste est triée, en utilisant la comparaison spécifiée /// /// Le type des éléments de la liste /// La liste à vérifier /// La comparaison à utiliser /// true si la liste est triée, false sinon public static bool IsOrdered(this IEnumerable list, Comparison comparison) { bool isFirstPass = true; TSource previous = default(TSource); foreach (TSource item in list) { if (!isFirstPass) { if (comparison(previous, item) > 0) return false; } isFirstPass = false; previous = item; } return true; } /// /// Vérifie que la liste est triée, en utilisant le comparateur par défaut /// /// Le type des éléments de la liste /// La liste à vérifier /// true si la liste est triée, false sinon public static bool IsOrdered(this IEnumerable list) { return list.IsOrdered(Comparer.Default); } /// /// Vérifie que la liste est triée en ordre descendant, en utilisant le comparateur spécifié /// /// Le type des éléments de la liste /// La liste à vérifier /// Le comparateur à utiliser /// true si la liste est triée, false sinon public static bool IsOrderedDescending(this IEnumerable list, IComparer comparer) { return list.IsOrdered(comparer.Reverse()); } /// /// Vérifie que la liste est triée en ordre descendant, en utilisant le comparateur par défaut /// /// Le type des éléments de la liste /// La liste à vérifier /// true si la liste est triée, false sinon public static bool IsOrderedDescending(this IEnumerable list) { return list.IsOrdered(Comparer.Default.Reverse()); } /// /// Vérifie que la liste est triée selon la clé de tri spécifiée, en utilisant le comparateur spécifié /// /// Le type des éléments de la liste /// Le type de la clé de tri /// La liste à vérifier /// Le comparateur à utiliser /// Une fonction pour extraire la clé de tri de l'élément /// true si la liste est triée selon le critère spécifié, false sinon public static bool IsOrderedBy(this IEnumerable list, Func keySelector, IComparer comparer) { return list.Select(keySelector).IsOrdered(comparer); } /// /// Vérifie que la liste est triée selon la clé de tri spécifiée, en utilisant le comparateur par défaut /// /// Le type des éléments de la liste /// Le type de la clé de tri /// La liste à vérifier /// Une fonction pour extraire la clé de tri de l'élément /// true si la liste est triée selon le critère spécifié, false sinon public static bool IsOrderedBy(this IEnumerable list, Func keySelector) { return list.IsOrderedBy(keySelector, Comparer.Default); } /// /// Vérifie que la liste est triée en ordre descendant selon la clé de tri spécifiée, en utilisant le comparateur spécifié /// /// Le type des éléments de la liste /// Le type de la clé de tri /// La liste à vérifier /// Le comparateur à utiliser /// Une fonction pour extraire la clé de tri de l'élément /// true si la liste est triée selon le critère spécifié, false sinon public static bool IsOrderedByDescending(this IEnumerable list, Func keySelector, IComparer comparer) { return list.IsOrderedBy(keySelector, comparer.Reverse()); } /// /// Vérifie que la liste est triée en ordre descendant selon la clé de tri spécifiée, en utilisant le comparateur par défaut /// /// Le type des éléments de la liste /// Le type de la clé de tri /// La liste à vérifier /// Une fonction pour extraire la clé de tri de l'élément /// true si la liste est triée selon le critère spécifié, false sinon public static bool IsOrderedByDescending(this IEnumerable list, Func keySelector) { return list.IsOrderedBy(keySelector, Comparer.Default.Reverse()); } #endregion #region AggregateByPairs /// /// Applique une fonction d'accumulation sur une séquence, en prenant les /// éléments consécutifs 2 par 2. La valeur spécifiée pour seed est /// utilisée comme valeur initiale de l'accumulateur. /// /// Type des éléments de la séquence /// Type de l'accumulateur /// Séquence sur laquelle appliquer l'accumulation /// Valeur initiale de l'accumulateur /// Fonction d'accumulation /// La valeur finale de l'accumulateur /// La fonction d'accumulation prend en paramètres, dans cet ordre: /// la valeur courante de l'accumulateur, l'élément courant et l'élément précédent. public static TAccumulate AggregateByPairs( this IEnumerable source, TAccumulate seed, Func func) { return AggregateByPairs(source, seed, func, LinqHelper.Identity); } /// /// Applique une fonction d'accumulation sur une séquence, en prenant les /// éléments consécutifs 2 par 2. La valeur spécifiée pour seed est /// utilisée comme valeur initiale de l'accumulateur, et la fonction spécifiée /// est utilisée pour sélectionner le résultat final /// /// Type des éléments de la séquence /// Type de l'accumulateur /// Type du résultat final /// Séquence sur laquelle appliquer l'accumulation /// Valeur initiale de l'accumulateur /// Fonction d'accumulation /// Fonction qui transforme la valeur finale de l'accumulateur en le résultat final /// La valeur finale de l'accumulateur transformée par resultSelector /// La fonction d'accumulation prend en paramètres, dans cet ordre: /// la valeur courante de l'accumulateur, l'élément courant et l'élément précédent. public static TResult AggregateByPairs( this IEnumerable source, TAccumulate seed, Func func, Func resultSelector) { TAccumulate acc = seed; TSource prev = default(TSource); bool first = true; foreach (var item in source) { if (!first) acc = func(acc, item, prev); first = false; prev = item; } return resultSelector(acc); } #endregion #region SelectAggregate // TODO: nom à revoir (Scan ?) /// /// Applique une fonction d'accumulation sur une séquence, en renvoyant la valeur /// de l'accumulateur à chaque étape. /// /// Type des éléments de source /// Type de l'accumulateur /// Séquence sur laquelle appliquer l'accumulation /// Valeur initiale de l'accumulateur /// Fonction d'accumulation à appeler sur chaque élément /// Séquence des valeurs de l'accumulateur à chaque étape. /// Cette méthode est similaire à Enumerable.Aggregate, mais cette dernière ne renvoie que le résultat /// final, alors que SelectAggregate renvoie le résultat de chaque étape. public static IEnumerable SelectAggregate( this IEnumerable source, TAccumulate seed, Func func) { source.CheckArgumentNull("source"); func.CheckArgumentNull("func"); return source.SelectAggregateIterator(seed, func); } private static IEnumerable SelectAggregateIterator( this IEnumerable source, TAccumulate seed, Func func) { TAccumulate previous = seed; foreach (var item in source) { TAccumulate result = func(previous, item); previous = result; yield return result; } } /// /// Applique une fonction d'accumulation sur une séquence, en renvoyant /// la valeur de l'accumulateur à chaque étape. /// /// Type des éléments de source /// Type de l'accumulateur /// Séquence sur laquelle appliquer l'accumulation /// Fonction d'accumulation à appeler sur chaque élément /// Séquence des valeurs de l'accumulateur à chaque étape. /// Cette méthode est similaire à Enumerable.Aggregate, mais cette dernière ne renvoie que le résultat /// final, alors que SelectAggregate renvoie le résultat de chaque étape. public static IEnumerable SelectAggregate( this IEnumerable source, Func func) { return source.SelectAggregate(default(TAccumulate), func); } /// /// Applique une fonction d'accumulation sur une séquence, en renvoyant /// la valeur de l'accumulateur à chaque étape. La fonction d'accumulation /// prend en paramètre les 2 valeurs précédentes de l'accumulateur. /// /// Type des éléments de source /// Type de l'accumulateur /// Séquence sur laquelle appliquer l'accumulation /// Valeur initiale de l'accumulateur (itération n-1) /// Valeur initiale de l'accumulateur (itération n-2) /// Fonction d'accumulation à appeler sur chaque élément /// Séquence des valeurs de l'accumulateur à chaque étape. public static IEnumerable SelectAggregate( this IEnumerable source, TAccumulate seed1, TAccumulate seed2, Func func) { source.CheckArgumentNull("source"); func.CheckArgumentNull("func"); return source.SelectAggregateIterator(seed1, seed2, func); } private static IEnumerable SelectAggregateIterator( this IEnumerable source, TAccumulate seed1, TAccumulate seed2, Func func) { TAccumulate previous1 = seed1; TAccumulate previous2 = seed2; foreach (var item in source) { TAccumulate result = func(previous1, previous2, item); previous2 = previous1; previous1 = result; yield return result; } } /// /// Applique une fonction d'accumulation sur une séquence, en renvoyant /// la valeur de l'accumulateur à chaque étape. La fonction d'accumulation /// prend en paramètre les 2 valeurs précédentes de l'accumulateur. /// /// Type des éléments de source /// Type de l'accumulateur /// Séquence sur laquelle appliquer l'accumulation /// Fonction d'accumulation à appeler sur chaque élément /// Séquence des valeurs de l'accumulateur à chaque étape. public static IEnumerable SelectAggregate( this IEnumerable source, Func func) { return source.SelectAggregate(default(TAccumulate), default(TAccumulate), func); } #endregion #region FirstOrDefault, LastOrDefault, ElementAtOrDefault, SingleOrDefault /// /// Retourne le premier élément d'une séquence, ou la valeur par défaut spécifiée si la séquence ne contient aucun élément. /// /// Type des éléments de source /// Séquence à partir de laquelle retourner le premier élément /// La valeur par défaut à renvoyer si la séquence est vide /// Le premier élément de source s'il existe, sinon la valeur par défaut spécifiée public static TSource FirstOrDefault(this IEnumerable source, TSource defaultValue) { return source.DefaultIfEmpty(defaultValue).First(); } /// /// Retourne le premier élément de la séquence à satisfaire à une condition, ou la valeur par défaut spécifiée /// si aucun élément correspondant n'est trouvé. /// /// Type des éléments de source /// Séquence à partir de laquelle retourner un élément /// Condition à vérifier /// La valeur par défaut à renvoyer si aucun élément ne satisfait la condition /// Le premier élément de source à satisfaire la condition, ou la valeur par défaut spécifiée si aucun /// élément ne satisfait la condition public static TSource FirstOrDefault(this IEnumerable source, Func predicate, TSource defaultValue) { return source.Where(predicate).FirstOrDefault(defaultValue); } /// /// Retourne le dernier élément d'une séquence, ou la valeur par défaut spécifiée si la séquence ne contient aucun élément. /// /// Type des éléments de source /// Séquence à partir de laquelle retourner le dernier élément /// La valeur par défaut à renvoyer si la séquence est vide /// Le dernier élément de source s'il existe, sinon la valeur par défaut spécifiée public static TSource LastOrDefault(this IEnumerable source, TSource defaultValue) { return source.DefaultIfEmpty(defaultValue).Last(); } /// /// Retourne le dernier élément de la séquence à satisfaire à une condition, ou la valeur par défaut spécifiée /// si aucun élément correspondant n'est trouvé. /// /// Type des éléments de source /// Séquence à partir de laquelle retourner un élément /// Condition à vérifier /// La valeur par défaut à renvoyer si aucun élément ne satisfait la condition /// Le dernier élément de source à satisfaire la condition, ou la valeur par défaut spécifiée si aucun /// élément ne satisfait la condition public static TSource LastOrDefault(this IEnumerable source, Func predicate, TSource defaultValue) { return source.Where(predicate).LastOrDefault(defaultValue); } /// /// Retourne l'élément situé à un index spécifié dans une séquence, ou la valeur par défaut spécifiée si l'index est hors limites. /// /// Type des éléments de source /// Séquence à partir de laquelle retourner un élément /// Index de l'élément à récupérer /// Valeur par défaut à renvoyer si l'index est hors-limite /// L'élément à l'index spécifié s'il existe ; sinon, la valeur par défaut spécifiée. public static TSource ElementAtOrDefault(this IEnumerable source, int index, TSource defaultValue) { if (index < 0) throw new ArgumentOutOfRangeException("index"); return source.Skip(index).DefaultIfEmpty(defaultValue).First(); } /// /// Retourne l'unique élément d'une séquence, ou la valeur par défaut spécifiée si la séquence n'a aucun élément. /// Lève une exception si la séquence contient plus d'un élément /// /// Type des éléments de source /// Séquence à partir de laquelle retourner un élément /// Valeur par défaut à retourner si la séquence est vide /// L'unique élément de la séquence, ou la valeur par défaut spécifiée si la séquence est vide public static TSource SingleOrDefault(this IEnumerable source, TSource defaultValue) { int i = 0; TSource value = defaultValue; foreach (var item in source) { if (i == 0) { value = item; } else { throw new InvalidOperationException(ExceptionMessages.InputSequenceHasMoreThanOneElement); } i++; } return value; } /// /// Retourne l'unique élément d'une séquence qui satisfait une condition, ou la valeur par défaut /// spécifiée si aucun élément ne satisfait la condition. /// Lève une exception si plusieurs éléments satisfont la condition. /// /// Type des éléments de source /// Séquence à partir de laquelle retourner un élément /// La condition à vérifier /// Valeur par défaut à retourner si aucun élément ne satisfait la condition /// L'unique élément de la séquence, ou la valeur par défaut spécifiée si aucun élément ne satisfait la condition public static TSource SingleOrDefault(this IEnumerable source, Func predicate, TSource defaultValue) { return source.Where(predicate).SingleOrDefault(defaultValue); } #endregion #region MaxBy, MinBy private static void ThrowEmptySequence() { throw new InvalidOperationException(ExceptionMessages.SequenceContainsNoElements); } /// /// Renvoie l'élément de la séquence ayant la plus grande valeur pour la fonction spécifiée /// /// Type des éléments de la séquence /// Type de retour de la fonction à évaluer /// Séquence d'éléments /// Fonction évaluée pour chaque élément /// L'élément ayant la plus grande valeur pour la fonction spécifiée public static TSource MaxBy(this IEnumerable source, Func selector) { source.CheckArgumentNull("source"); selector.CheckArgumentNull("selector"); return source.ExtremeBy(selector, null, 1); } /// /// Renvoie l'élément de la séquence ayant la plus grande valeur pour la fonction spécifiée /// /// Type des éléments de la séquence /// Type de retour de la fonction à évaluer /// Séquence d'éléments /// Fonction évaluée pour chaque élément /// Comparateur à utiliser pour le résultat du sélecteur. Si null, le comparateur par défaut sera utilisé /// L'élément ayant la plus grande valeur pour la fonction spécifiée public static TSource MaxBy(this IEnumerable source, Func selector, IComparer comparer) { source.CheckArgumentNull("source"); selector.CheckArgumentNull("selector"); return source.ExtremeBy(selector, comparer, 1); } /// /// Renvoie l'élément de la séquence ayant la plus petite valeur pour la fonction spécifiée /// /// Type des éléments de la séquence /// Type de retour de la fonction à évaluer /// Séquence d'éléments /// Fonction évaluée pour chaque élément /// L'élément ayant la plus petite valeur pour la fonction spécifiée public static TSource MinBy(this IEnumerable source, Func selector) { source.CheckArgumentNull("source"); selector.CheckArgumentNull("selector"); return source.ExtremeBy(selector, null, -1); } /// /// Renvoie l'élément de la séquence ayant la plus petite valeur pour la fonction spécifiée /// /// Type des éléments de la séquence /// Type de retour de la fonction à évaluer /// Séquence d'éléments /// Fonction évaluée pour chaque élément /// Comparateur à utiliser pour le résultat du sélecteur. Si null, le comparateur par défaut sera utilisé /// L'élément ayant la plus petite valeur pour la fonction spécifiée public static TSource MinBy(this IEnumerable source, Func selector, IComparer comparer) { source.CheckArgumentNull("source"); selector.CheckArgumentNull("selector"); return source.ExtremeBy(selector, comparer, -1); } private static TSource ExtremeBy(this IEnumerable source, Func selector, IComparer comparer, int sign) { comparer = comparer ?? Comparer.Default; TKey extremeValue = default(TKey); TSource extreme = default(TSource); bool first = true; foreach (var item in source) { TKey value = selector(item); int compare = comparer.Compare(value, extremeValue); if (Math.Sign(compare) == sign || first) { extremeValue = value; extreme = item; } first = false; } if (first) ThrowEmptySequence(); return extreme; } /// /// Renvoie tous les éléments de la séquence ayant la plus grande valeur pour la fonction spécifiée /// /// Type des éléments de la séquence /// Type de retour de la fonction à évaluer /// Séquence d'éléments /// Fonction évaluée pour chaque élément /// Collection des éléments ayant la plus grande valeur pour la fonction spécifiée public static IEnumerable AllMaxBy(this IEnumerable source, Func selector) { source.CheckArgumentNull("source"); selector.CheckArgumentNull("selector"); return source.AllExtremesBy(selector, null, 1); } /// /// Renvoie tous les éléments de la séquence ayant la plus grande valeur pour la fonction spécifiée /// /// Type des éléments de la séquence /// Type de retour de la fonction à évaluer /// Séquence d'éléments /// Fonction évaluée pour chaque élément /// Comparateur à utiliser pour le résultat du sélecteur. Si null, le comparateur par défaut sera utilisé /// Collection des éléments ayant la plus grande valeur pour la fonction spécifiée public static IEnumerable AllMaxBy(this IEnumerable source, Func selector, IComparer comparer) { source.CheckArgumentNull("source"); selector.CheckArgumentNull("selector"); return source.AllExtremesBy(selector, comparer, 1); } /// /// Renvoie tous les éléments de la séquence ayant la plus petite valeur pour la fonction spécifiée /// /// Type des éléments de la séquence /// Type de retour de la fonction à évaluer /// Séquence d'éléments /// Fonction évaluée pour chaque élément /// Collection des éléments ayant la plus petite valeur pour la fonction spécifiée public static IEnumerable AllMinBy(this IEnumerable source, Func selector) { source.CheckArgumentNull("source"); selector.CheckArgumentNull("selector"); return source.AllExtremesBy(selector, null, -1); } /// /// Renvoie tous les éléments de la séquence ayant la plus petite valeur pour la fonction spécifiée /// /// Type des éléments de la séquence /// Type de retour de la fonction à évaluer /// Séquence d'éléments /// Fonction évaluée pour chaque élément /// Comparateur à utiliser pour le résultat du sélecteur. Si null, le comparateur par défaut sera utilisé /// Collection des éléments la plus petite valeur pour la fonction spécifiée public static IEnumerable AllMinBy(this IEnumerable source, Func selector, IComparer comparer) { source.CheckArgumentNull("source"); selector.CheckArgumentNull("selector"); return source.AllExtremesBy(selector, comparer, -1); } private static IEnumerable AllExtremesBy(this IEnumerable source, Func selector, IComparer comparer, int sign) { comparer = comparer ?? Comparer.Default; TKey extremeValue = default(TKey); List extremes = new List(); bool first = true; foreach (var item in source) { TKey value = selector(item); int compare = comparer.Compare(value, extremeValue); if (first) { extremeValue = value; extremes.Add(item); } else if (Math.Sign(compare) == sign) { extremes.Clear(); extremeValue = value; extremes.Add(item); } else if (compare == 0) { extremes.Add(item); } first = false; } return extremes.ToArray(); } #endregion #region Max, Min (with comparer) /// /// Renvoie la valeur maximale de la séquence selon le comparateur spécifié /// /// Type des éléments de la liste /// Séquence d'éléments /// Comparateur à utiliser /// La valeur maximale de la séquence selon le comparateur spécifié public static TSource Max(this IEnumerable source, IComparer comparer) { source.CheckArgumentNull("source"); return source.Extreme(comparer, 1); } /// /// Renvoie la valeur minimale de la séquence selon le comparateur spécifié /// /// Type des éléments de la liste /// Séquence d'éléments /// Comparateur à utiliser /// La valeur minimale de la séquence selon le comparateur spécifié public static TSource Min(this IEnumerable source, IComparer comparer) { source.CheckArgumentNull("source"); return source.Extreme(comparer, -1); } private static TSource Extreme(this IEnumerable source, IComparer comparer, int sign) { comparer = comparer ?? Comparer.Default; TSource extreme = default(TSource); bool first = true; foreach (var item in source) { int compare = comparer.Compare(item, extreme); if (Math.Sign(compare) == sign || first) { extreme = item; } first = false; } if (first) ThrowEmptySequence(); return extreme; } #endregion #region AsIndexed, Unindex /// /// Associe un index à un élément d'une séquence /// /// Type des éléments de la séquence public class IndexedItem { internal IndexedItem(int index, T value) { Index = index; Value = value; } /// /// Index de l'élément /// public int Index { get; private set; } /// /// Valeur de l'élément /// public T Value { get; private set; } } /// /// Associe à chaque élément de la séquence son index dans la séquence /// /// Type des éléments de la séquence /// Séquence d'éléments à indexer /// Une séquence projetée où chaque élément est associé à son index public static IEnumerable> AsIndexed(this IEnumerable source) { return source.Select((v, i) => new IndexedItem(i, v)); } /// /// Dissocie chaque élément de la séquence de son index. Cette méthode effectue l'opération inverse de AsIndexed. /// /// Type des éléments de la séquence /// Séquence d'éléments à dissocier de leur index /// Une séquence d'éléments sans leur index public static IEnumerable Unindex(this IEnumerable> source) { return source.Select(x => x.Value); } #endregion #region TakeEvery, SkipEvery, GroupEvery /// /// Extrait un élément sur frequency à partir de la séquence d'entrée, à partir du premier élément. /// /// Type des éléments de la séquence /// Séquence d'entrée /// Fréquence des éléments à extraire /// Une séquence contenant un élément sur frequency de la séquence d'entrée public static IEnumerable TakeEvery(this IEnumerable source, int frequency) { return source.TakeEvery(frequency, 0); } /// /// Extrait un élément sur frequency à partir de la séquence d'entrée, à partir de la position spécifiée. /// /// Type des éléments de la séquence /// Séquence d'entrée /// Fréquence des éléments à extraire /// Position du premier élément à extraire /// Une séquence contenant un élément sur frequency de la séquence d'entrée à partir de la position spécifiée public static IEnumerable TakeEvery(this IEnumerable source, int frequency, int start) { if (frequency <= 0) throw new ArgumentException("frequency"); if (start < 0) throw new ArgumentException("start"); return source .Skip(start) .Where((item, index) => index % frequency == 0); } /// /// Renvoie les éléments de la séquence d'entrée, en sautant un élément sur frequency à partir de la position spécifiée. /// /// Type des éléments de la séquence /// Séquence d'entrée /// Fréquence des éléments à sauter /// Une séquence les éléments de la séquence d'entrée, en en sautant un sur frequency à partir de la position spécifiée public static IEnumerable SkipEvery(this IEnumerable source, int frequency) { return source.SkipEvery(frequency, 0); } /// /// Renvoie les éléments de la séquence d'entrée, en sautant un élément sur frequency à partir du premier élément. /// /// Type des éléments de la séquence /// Séquence d'entrée /// Fréquence des éléments à sauter /// Position du premier élément à sauter /// Une séquence les éléments de la séquence d'entrée, en en sautant un sur frequency à partir du premier. public static IEnumerable SkipEvery(this IEnumerable source, int frequency, int start) { if (frequency <= 0) throw new ArgumentException("frequency"); return source .Where((item, index) => index < start ? true : (index - start) % frequency != 0); } /// /// Renvoie les éléments de la séquence d'entrée par groupes de groupLength éléments. /// /// Type des éléments de la séquence /// Séquence d'éléments à grouper /// Longueur des groupes /// Une séquence de groupes de groupLength éléments /// Si le nombre total d'éléments dans la séquence n'est pas un multiple de groupLength, le dernier groupe renvoyé contiendra moins de groupLength éléments. public static IEnumerable> GroupEvery(this IEnumerable source, int groupLength) { if (groupLength <= 0) throw new ArgumentOutOfRangeException("groupLength"); return source .AsIndexed() .GroupBy(x => x.Index / groupLength) .Select(g => g.Unindex()); } #endregion #region SkipFirst, SkipLast, SkipAt /// /// Renvoie les éléments de la séquence d'entrée en sautant le premier. /// /// Type des éléments de la séquence /// Séquence d'entrée /// La séquence d'entrée sans le premier élément public static IEnumerable SkipFirst(this IEnumerable source) { source.CheckArgumentNull("source"); return source.Skip(1); } /// /// Renvoie les éléments de la séquence d'entrée en sautant le premier élément qui vérifie le prédicat spécifié. /// /// Type des éléments de la séquence /// Séquence d'entrée /// Prédicat à évaluer pour déterminer s'il faut sauter l'élément /// La séquence d'entrée sans le premier élément qui vérifie le prédicat public static IEnumerable SkipFirst(this IEnumerable source, Func predicate) { source.CheckArgumentNull("source"); predicate.CheckArgumentNull("predicate"); return source.SkipFirstIterator(predicate); } private static IEnumerable SkipFirstIterator(this IEnumerable source, Func predicate) { bool skipped = false; foreach (var item in source) { if (!skipped && predicate(item)) { skipped = true; continue; } yield return item; } } /// /// Renvoie les éléments de la séquence d'entrée en sautant le dernier élément qui vérifie le prédicat spécifié. /// /// Type des éléments de la séquence /// Séquence d'entrée /// Prédicat à évaluer pour déterminer s'il faut sauter l'élément /// La séquence d'entrée sans le dernier élément qui vérifie le prédicat public static IEnumerable SkipLast(this IEnumerable source, Func predicate) { source.CheckArgumentNull("source"); predicate.CheckArgumentNull("predicate"); return source.SkipLastIterator(predicate); } private static IEnumerable SkipLastIterator(this IEnumerable source, Func predicate) { var queue = new Queue(); var last = default(TSource); bool any = false; foreach (var item in source) { if (predicate(item)) { if (any) yield return last; while (queue.Count > 0) yield return queue.Dequeue(); last = item; any = true; } else { if (any) queue.Enqueue(item); else yield return item; } } while (queue.Count > 0) yield return queue.Dequeue(); } /// /// Renvoie les éléments de la séquence d'entrée en sautant l'élément qui se trouve à la position spécifiée. /// /// Type des éléments de la séquence /// Séquence d'entrée /// Position de l'élément à sauter /// La séquence d'entrée sans l'élément qui se trouvait à la position spécifiée public static IEnumerable SkipAt(this IEnumerable source, int index) { return source.Where((it, i) => i != index); } #endregion #region TakeLast /// /// Renvoie le nombre spécifié d'éléments de la fin d'une séquence /// /// Type des éléments de la séquence /// Séquence d'entrée /// Nombre maximum d'éléments à renvoyer /// Les count derniers éléments de la séquence public static IEnumerable TakeLast(this IEnumerable source, int count) { source.CheckArgumentNull("source"); if (count < 0) throw new ArgumentOutOfRangeException( "count", string.Format(ExceptionMessages.NumberMustBePositiveOrZero, "count")); return source.TakeLastIterator(count); } private static IEnumerable TakeLastIterator(this IEnumerable source, int count) { if (count <= 0) yield break; IList list = source as IList; if (list != null) { count = Math.Min(count, list.Count); for (int i = count; i > 0; i--) { yield return list[list.Count - i]; } } else { var queue = new Queue(); foreach (var item in source) { if (queue.Count == count) queue.Dequeue(); queue.Enqueue(item); } foreach (var item in queue) yield return item; } } #endregion #region Replace, ReplaceAt /// /// Remplace toutes les occurrences de l'élément spécifié dans une séquence par un autre élément spécifié. /// /// Type des éléments de la séquence /// Séquence d'entrée /// Elément à remplacer /// Elément qui remplace l'ancien élément /// Séquence dans laquelle l'ancien élément a été remplacé par le nouveau public static IEnumerable Replace(this IEnumerable source, TSource oldItem, TSource newItem) { return source.Replace(oldItem, newItem, null); } /// /// Remplace toutes les occurrences de l'élément spécifié dans une séquence par un autre élément spécifié, /// en utilisant le comparateur d'égalité spécifié /// /// Type des éléments de la séquence /// Séquence d'entrée /// Elément à remplacer /// Elément qui remplace l'ancien élément /// Comprateur d'égalité à utiliser /// Séquence dans laquelle l'ancien élément a été remplacé par le nouveau public static IEnumerable Replace(this IEnumerable source, TSource oldItem, TSource newItem, IEqualityComparer comparer) { comparer = comparer ?? EqualityComparer.Default; return source.Select((it, i) => comparer.Equals(it, oldItem) ? newItem : it); } /// /// Remplace l'élément à la position spécifiée d'une séquence par un élément spécifié. /// /// Type des éléments de la séquence /// Séquence d'entrée /// Position à laquelle effectuer le remplacement /// Elément qui remplace l'élément à la position spécifiée /// Séquence dans laquelle un élément a été remplacé public static IEnumerable ReplaceAt(this IEnumerable source, int index, TSource item) { return source.Select((it, i) => i == index ? item : it); } #endregion #region InsertAt, InsertBefore(First), InsertAfter(First) /// /// Insère un élément à la position spécifiée d'une séquence. /// /// Type des éléments de la séquence /// Séquence d'entrée /// Position à laquelle insérer l'élément /// Elément à insérer /// Séquence dans laquelle un élément a été inséré public static IEnumerable InsertAt(this IEnumerable source, int index, TSource item) { source.CheckArgumentNull("source"); index.CheckArgumentOutOfRange("index", 0, int.MaxValue); return source.InsertAtIterator(index, item); } private static IEnumerable InsertAtIterator(this IEnumerable source, int index, TSource item) { int i = 0; foreach (var it in source) { if (i++ == index) yield return item; yield return it; } } /// /// Insère un nouvel élément dans une séquence juste avant la première occurrence de l'élément de référence spécifié /// ///Type des éléments de la séquence ///Séquence d'entrée ///Elément de référence avant lequel on insère le nouvel élément ///Nouvel élément à insérer ///Séquence avec le nouvel élément placé avant la première occurrence de l'élément de référence public static IEnumerable InsertBeforeFirst(this IEnumerable source, TSource refItem, TSource newItem) { return source.InsertBeforeFirst(refItem, newItem, null); } /// /// Insère un nouvel élément dans une séquence juste avant la première occurrence de l'élément de référence spécifié, /// en utilisant le comparateur d'égalité spécifié. /// ///Type des éléments de la séquence ///Séquence d'entrée ///Elément de référence avant lequel on insère le nouvel élément ///Nouvel élément à insérer /// Comparateur d'égalité à utiliser ///Séquence avec le nouvel élément placé avant la première occurrence de l'élément de référence public static IEnumerable InsertBeforeFirst(this IEnumerable source, TSource refItem, TSource newItem, IEqualityComparer comparer) { source.CheckArgumentNull("source"); return source.InsertBeforeFirstIterator(refItem, newItem, comparer); } private static IEnumerable InsertBeforeFirstIterator(this IEnumerable source, TSource refItem, TSource newItem, IEqualityComparer comparer) { comparer = comparer ?? EqualityComparer.Default; bool inserted = false; foreach (var item in source) { if (!inserted && comparer.Equals(item, refItem)) { yield return newItem; inserted = true; } yield return item; } } /// /// Insère un nouvel élément dans une séquence juste après la première occurrence de l'élément de référence spécifié /// ///Type des éléments de la séquence ///Séquence d'entrée ///Elément de référence après lequel on insère le nouvel élément ///Nouvel élément à insérer ///Séquence avec le nouvel élément placé après la première occurrence de l'élément de référence public static IEnumerable InsertAfterFirst(this IEnumerable source, TSource refItem, TSource newItem) { return source.InsertAfterFirst(refItem, newItem, null); } /// /// Insère un nouvel élément dans une séquence juste après la première occurrence de l'élément de référence spécifié, /// en utilisant le comparateur d'égalité spécifié. /// ///Type des éléments de la séquence ///Séquence d'entrée ///Elément de référence après lequel on insère le nouvel élément ///Nouvel élément à insérer ///Comparateur d'égalité à utiliser ///Séquence avec le nouvel élément placé après la première occurrence de l'élément de référence public static IEnumerable InsertAfterFirst(this IEnumerable source, TSource refItem, TSource newItem, IEqualityComparer comparer) { source.CheckArgumentNull("source"); return source.InsertAfterFirstIterator(refItem, newItem, comparer); } private static IEnumerable InsertAfterFirstIterator(this IEnumerable source, TSource refItem, TSource newItem, IEqualityComparer comparer) { comparer = comparer ?? EqualityComparer.Default; bool inserted = false; foreach (var item in source) { yield return item; if (!inserted && comparer.Equals(item, refItem)) { yield return newItem; inserted = true; } } } /// /// Insère un nouvel élément dans une séquence avant chaque occurrence de l'élément de référence spécifié /// ///Type des éléments de la séquence ///Séquence d'entrée ///Elément de référence avant lequel on insère le nouvel élément ///Nouvel élément à insérer ///Séquence avec le nouvel élément placé avant chaque occurrence de l'élément de référence public static IEnumerable InsertBefore(this IEnumerable source, TSource refItem, TSource newItem) { return source.InsertBefore(refItem, newItem, null); } /// /// Insère un nouvel élément dans une séquence avant chaque occurrence de l'élément de référence spécifié, /// en utilisant le comparateur d'égalité spécifié. /// ///Type des éléments de la séquence ///Séquence d'entrée ///Elément de référence avant lequel on insère le nouvel élément ///Nouvel élément à insérer /// Comparateur d'égalité à utiliser ///Séquence avec le nouvel élément placé avant chaque occurrence de l'élément de référence public static IEnumerable InsertBefore(this IEnumerable source, TSource refItem, TSource newItem, IEqualityComparer comparer) { source.CheckArgumentNull("source"); return source.InsertBeforeIterator(refItem, newItem, comparer); } private static IEnumerable InsertBeforeIterator(this IEnumerable source, TSource refItem, TSource newItem, IEqualityComparer comparer) { comparer = comparer ?? EqualityComparer.Default; foreach (var item in source) { if (comparer.Equals(item, refItem)) yield return newItem; yield return item; } } /// /// Insère un nouvel élément dans une séquence après chaque occurrence de l'élément de référence spécifié /// ///Type des éléments de la séquence ///Séquence d'entrée ///Elément de référence après lequel on insère le nouvel élément ///Nouvel élément à insérer ///Séquence avec le nouvel élément placé après chaque occurrence de l'élément de référence public static IEnumerable InsertAfter(this IEnumerable source, TSource refItem, TSource newItem) { return source.InsertAfter(refItem, newItem, null); } /// /// Insère un nouvel élément dans une séquence après chaque occurrence de l'élément de référence spécifié, /// en utilisant le comparateur d'égalité spécifié. /// ///Type des éléments de la séquence ///Séquence d'entrée ///Elément de référence après lequel on insère le nouvel élément ///Nouvel élément à insérer ///Comparateur d'égalité à utiliser ///Séquence avec le nouvel élément placé après chaque occurrence de l'élément de référence public static IEnumerable InsertAfter(this IEnumerable source, TSource refItem, TSource newItem, IEqualityComparer comparer) { source.CheckArgumentNull("source"); return source.InsertAfterIterator(refItem, newItem, comparer); } private static IEnumerable InsertAfterIterator(this IEnumerable source, TSource refItem, TSource newItem, IEqualityComparer comparer) { comparer = comparer ?? EqualityComparer.Default; foreach (var item in source) { yield return item; if (comparer.Equals(item, refItem)) yield return newItem; } } #endregion #region None /// /// Indique si une séquence ne contient aucun élément /// /// Type des éléments de la séquence /// Séquence à vérifier /// true si source ne contient aucun élément ; sinon, false public static bool None(this IEnumerable source) { return !source.Any(); } /// /// Indique s'il n'existe dans la séquence aucun élément qui satisfait le prédicat /// /// Type des éléments de la séquence /// Séquence à vérifier /// Prédicat à évaluer pour chaque élément de la séquence /// true si la séquence ne contient aucun élément qui satisfait le prédicat ; sinon, false public static bool None(this IEnumerable source, Func predicate) { return !source.Any(predicate); } #endregion #region ContainsAny /// /// Indique si une séquence contient au moins un des éléments spécifiés, en utilisant le comparateur par défaut /// /// Type des élements de la séquence /// Séquence dans laquelle rechercher des éléments /// Eléments à rechercher /// true si source contient au moins un des éléments de items, false sinon public static bool ContainsAny(this IEnumerable source, IEnumerable items) { return ContainsAny(source, items, EqualityComparer.Default); } /// /// Indique si une séquence contient au moins un des éléments spécifiés, en utilisant le comparateur spécifié /// /// Type des élements de la séquence /// Séquence dans laquelle rechercher des éléments /// Eléments à rechercher /// Comparateur à utiliser /// true si source contient au moins un des éléments de items, false sinon public static bool ContainsAny(this IEnumerable source, IEnumerable items, IEqualityComparer comparer) { return source.Intersect(items, comparer).Any(); } #endregion #region IndexOf /// /// Renvoie l'index de la première occurence de l'élément spécifié dans la séquence /// /// Le type des éléments de la séquence /// La séquence dans laquelle chercher l'élément /// L'élément à rechercher /// L'index de la première occurence de l'élément dans la séquence, ou -1 si l'élément ne s'y trouve pas. public static int IndexOf(this IEnumerable source, T item) { return source.IndexOf(item, null); } /// /// Renvoie l'index de la première occurence de l'élément spécifié dans la séquence, en utilisant le comparateur spécifié. /// /// Le type des éléments de la séquence /// La séquence dans laquelle chercher l'élément /// L'élément à rechercher /// Comparateur à utiliser pour tester l'égalité des éléments /// L'index de la première occurence de l'élément dans la séquence, ou -1 si l'élément ne s'y trouve pas. public static int IndexOf(this IEnumerable source, T item, IEqualityComparer comparer) { source.CheckArgumentNull("source"); comparer = comparer ?? EqualityComparer.Default; return source.AsIndexed() .Where(x => comparer.Equals(x.Value, item)) .Select(x => x.Index) .FirstOrDefault(-1); } /// /// Renvoie l'index du premier élément pour lequel le prédicat spécifié est vérifié /// /// Le type des éléments de la séquence /// La séquence dans laquelle chercher l'élément /// Le prédicat à évaluer /// L'index du premier élément pour lequel le prédicat est vérifié, ou -1 si aucun élément ne vérifie le prédicat. public static int IndexOf(this IEnumerable source, Func predicate) { return source.AsIndexed() .Where(x => predicate(x.Value)) .Select(x => x.Index) .FirstOrDefault(-1); } /// /// Renvoie l'index de la dernière occurence de l'élément spécifié dans la séquence /// /// Le type des éléments de la séquence /// La séquence dans laquelle chercher l'élément /// L'élément à rechercher /// L'index de la dernière occurence de l'élément dans la séquence, ou -1 si l'élément ne s'y trouve pas. public static int LastIndexOf(this IEnumerable source, T item) { return source.LastIndexOf(item, null); } /// /// Renvoie l'index de la dernière occurence de l'élément spécifié dans la séquence, en utilisant le comparateur spécifié. /// /// Le type des éléments de la séquence /// La séquence dans laquelle chercher l'élément /// L'élément à rechercher /// Comparateur à utiliser pour tester l'égalité des éléments /// L'index de la dernière occurence de l'élément dans la séquence, ou -1 si l'élément ne s'y trouve pas. public static int LastIndexOf(this IEnumerable source, T item, IEqualityComparer comparer) { source.CheckArgumentNull("source"); comparer = comparer ?? EqualityComparer.Default; return source.AsIndexed() .Where(x => comparer.Equals(x.Value, item)) .Select(x => x.Index) .LastOrDefault(-1); } /// /// Renvoie l'index du dernier élément pour lequel le prédicat spécifié est vérifié /// /// Le type des éléments de la séquence /// La séquence dans laquelle chercher l'élément /// Le prédicat à évaluer /// L'index du dernier élément pour lequel le prédicat est vérifié, ou -1 si aucun élément ne vérifie le prédicat. public static int LastIndexOf(this IEnumerable source, Func predicate) { return source.AsIndexed() .Where(x => predicate(x.Value)) .Select(x => x.Index) .LastOrDefault(-1); } #endregion #region CopyTo /// /// Copie tous les éléments de la séquence vers le tableau spécifié /// /// Type des éléments de la séquence /// Séquence à partir de laquelle copier les éléments /// Tableau vers lequel copier les éléments /// Position dans le tableau à partir de laquelle les éléments sont copiés public static void CopyTo(this IEnumerable source, T[] array, int arrayIndex) { int lowerBound = array.GetLowerBound(0); int upperBound = array.GetUpperBound(0); if (arrayIndex < lowerBound || arrayIndex > upperBound) throw new ArgumentOutOfRangeException("arrayIndex"); int i = 0; foreach (var item in source) { if (arrayIndex + i > upperBound) throw new ArgumentException(ExceptionMessages.ArrayCapacityInsufficient); array[arrayIndex + i] = item; i++; } } #endregion #region Apply /// /// Applique l'action spécifiée "au passage" sur chaque élément de la séquence /// /// Type des éléments de la séquence /// Séquence d'origine /// Action à effectuer sur chaque élément de la séquence /// La séquence d'origine /// Cette méthode s'utilise uniquement avec des types référence. Pour les types valeur, /// le paramètre de l'action doit être passé par référence. public static IEnumerable Apply(this IEnumerable source, Action action) where T : class { action.CheckArgumentNull("action"); return source.ApplyIterator(action); } private static IEnumerable ApplyIterator(this IEnumerable source, Action action) where T : class { foreach (var item in source) { action(item); yield return item; } } /// /// Représente une action avec un paramètre passé par référence /// /// Type du paramètre /// Paramètre de l'action public delegate void ByRefAction(ref T param); /// /// Applique l'action spécifiée "au passage" sur chaque élément de la séquence /// /// Type des éléments de la séquence /// Séquence d'origine /// Action à effectuer sur chaque élément de la séquence /// La séquence d'origine /// Cette méthode s'utilise uniquement avec des types valeur. Pour les types référence, /// le paramètre de l'action doit être passé par valeur. public static IEnumerable Apply(this IEnumerable source, ByRefAction action) where T : struct { action.CheckArgumentNull("action"); return source.ApplyIterator(action); } private static IEnumerable ApplyIterator(this IEnumerable source, ByRefAction action) where T : struct { foreach (var item in source) { T it = item; action(ref it); yield return it; } } #endregion #region IndexOfSequence /// /// Renvoie la position de début de la première occurence de la sous-séquence spécifiée dans la séquence source, /// en utilisant le comparateur par défaut. /// /// Type des éléments de la séquence /// Séquence source dans laquelle on recherche une sous-séquence /// Sous-séquence à rechercher /// La position de la première occurence de sequence dans source, ou -1 si la sous-séquence n'est pas trouvée. public static int IndexOfSequence(this IEnumerable source, IEnumerable sequence) { return source.IndexOfSequence(sequence, null); } /// /// Renvoie la position de début de la première occurence de la sous-séquence spécifiée dans la séquence source, /// en utilisant le comparateur spécifié. /// /// Type des éléments de la séquence /// Séquence source dans laquelle on recherche une sous-séquence /// Sous-séquence à rechercher /// Comparateur à utiliser /// La position de la première occurence de sequence dans source, ou -1 si la sous-séquence n'est pas trouvée. public static int IndexOfSequence(this IEnumerable source, IEnumerable sequence, IEqualityComparer comparer) { source.CheckArgumentNull("source"); sequence.CheckArgumentNull("sequence"); comparer = comparer ?? EqualityComparer.Default; var seq = sequence.ToArray(); int p = 0; // current position in source sequence int i = 0; // current position in searched sequence var prospects = new List(); // list of prospective matches foreach (var item in source) { // ReSharper disable AccessToModifiedClosure // Remove bad prospective matches prospects.RemoveAll(k => !comparer.Equals(item, seq[p - k])); // ReSharper restore AccessToModifiedClosure // Is it the start of a prospective match ? if (comparer.Equals(item, seq[0])) { prospects.Add(p); } // Does current character continues partial match ? if (comparer.Equals(item, seq[i])) { i++; // Do we have a complete match ? if (i == seq.Length) { // Bingo ! return p - seq.Length + 1; } } else // Mismatch { // Do we have prospective matches to fall back to ? if (prospects.Count > 0) { // Yes, use the first one int k = prospects[0]; i = p - k + 1; } else { // No, start from beginning of searched sequence i = 0; } } p++; } // No match return -1; } #endregion #region Split /// /// Divise une séquence d'éléments en plusieurs parties à chaque fois qu'un séparateur /// est rencontré. /// /// Type des éléments de la séquence /// Séquence d'éléments à diviser /// Prédicat qui détermine si un élément de la liste est un séparateur /// Une séquence de parties de la séquence d'origine public static IEnumerable> Split(this IEnumerable source, Func isSeparator) { source.CheckArgumentNull("source"); isSeparator.CheckArgumentNull("isSeparator"); return source.SplitIterator(isSeparator); } private static IEnumerable> SplitIterator(this IEnumerable source, Func isSeparator) { List list = new List(); foreach (var item in source) { if (isSeparator(item)) { if (list.Count > 0) { yield return list.AsReadOnly(); list = new List(); } } else { list.Add(item); } } if (list.Count > 0) yield return list.AsReadOnly(); } #endregion #region DistinctBy /// /// Retourne des éléments distincts d'une séquence, en les comparant selon la clé spécifiée /// /// Type des éléments de la séquence /// Type de la clé /// Séquence de laquelle supprimer les éléments en double /// Fonction qui renvoie la clé à utiliser pour comparer les éléments /// Une séquence contenant des éléments distincts de la séquence d'origine public static IEnumerable DistinctBy(this IEnumerable source, Func keySelector) { return source.DistinctBy(keySelector, null); } /// /// Retourne des éléments distincts d'une séquence, en les comparant selon la clé spécifiée, /// et en comparant les clés avec le comparateur spécifié. /// /// Type des éléments de la séquence /// Type de la clé /// Séquence de laquelle supprimer les éléments en double /// Fonction qui renvoie la clé à utiliser pour comparer les éléments /// Comparateur à utiliser pour comparer les clés. Si null, le comparateur par défaut sera utilisé /// Une séquence contenant des éléments distincts de la séquence d'origine public static IEnumerable DistinctBy(this IEnumerable source, Func keySelector, IEqualityComparer keyComparer) { source.CheckArgumentNull("source"); keySelector.CheckArgumentNull("keySelector"); return source.Distinct(new KeyEqualityComparer(keySelector, keyComparer)); } #endregion #region ExceptBy /// /// Retourne des éléments de la première séquence qui n'appartiennent pas à la deuxième, en les comparant selon la clé spécifiée. /// /// Type des éléments de la séquence /// Type de la clé /// Première séquence /// Deuxième séquence /// Fonction qui renvoie la clé à utiliser pour comparer les éléments /// Une séquence contenant les éléments de la première séquence qui n'appartiennent pas à la deuxième public static IEnumerable ExceptBy(this IEnumerable source, IEnumerable other, Func keySelector) { return source.ExceptBy(other, keySelector, null); } /// /// Retourne des éléments de la première séquence qui n'appartiennent pas à la deuxième, en les comparant selon la clé spécifiée, /// et en comparant les clés avec le comparateur spécifié. /// /// Type des éléments de la séquence /// Type de la clé /// Première séquence /// Deuxième séquence /// Fonction qui renvoie la clé à utiliser pour comparer les éléments /// Comparateur à utiliser pour comparer les clés. Si null, le comparateur par défaut sera utilisé /// Une séquence contenant les éléments de la première séquence qui n'appartiennent pas à la deuxième public static IEnumerable ExceptBy(this IEnumerable source, IEnumerable other, Func keySelector, IEqualityComparer keyComparer) { source.CheckArgumentNull("source"); other.CheckArgumentNull("other"); keySelector.CheckArgumentNull("keySelector"); return source.Except(other, new KeyEqualityComparer(keySelector, keyComparer)); } #endregion #region UnionBy /// /// Produit l'union de deux séquences en comparant les éléments selon la clé spécifiée. /// /// Type des éléments de la séquence /// Type de la clé /// Première séquence /// Deuxième séquence /// Fonction qui renvoie la clé à utiliser pour comparer les éléments /// Séquence constituée des éléments des deux séquences d'entrée, sans les doublons public static IEnumerable UnionBy(this IEnumerable source, IEnumerable other, Func keySelector) { return source.UnionBy(other, keySelector, null); } /// /// Produit l'union de deux séquences en comparant les éléments selon la clé spécifiée, et en comparant les clés à l'aide du comparateur d'égalité spécifié. /// /// Type des éléments de la séquence /// Type de la clé /// Première séquence /// Deuxième séquence /// Fonction qui renvoie la clé à utiliser pour comparer les éléments /// Comparateur à utiliser pour comparer les clés. Si null, le comparateur par défaut sera utilisé /// Séquence constituée des éléments des deux séquences d'entrée, sans les doublons public static IEnumerable UnionBy(this IEnumerable source, IEnumerable other, Func keySelector, IEqualityComparer keyComparer) { source.CheckArgumentNull("source"); other.CheckArgumentNull("other"); keySelector.CheckArgumentNull("keySelector"); return source.Union(other, new KeyEqualityComparer(keySelector, keyComparer)); } #endregion #region IntersectBy /// /// Produit l'intersection de deux séquences en comparant les éléments selon la clé spécifiée. /// /// Type des éléments de la séquence /// Type de la clé /// Première séquence /// Deuxième séquence /// Fonction qui renvoie la clé à utiliser pour comparer les éléments /// Séquence constituée des éléments des deux séquences présents dans les deux séquences d'entrée, sans les doublons public static IEnumerable IntersectBy(this IEnumerable source, IEnumerable other, Func keySelector) { return source.IntersectBy(other, keySelector, null); } /// /// Produit l'intersection de deux séquences en comparant les éléments selon la clé spécifiée, et en comparant les clés à l'aide du comparateur d'égalité spécifié. /// /// Type des éléments de la séquence /// Type de la clé /// Première séquence /// Deuxième séquence /// Fonction qui renvoie la clé à utiliser pour comparer les éléments /// Comparateur à utiliser pour comparer les clés. Si null, le comparateur par défaut sera utilisé /// Séquence constituée des éléments des deux séquences présents dans les deux séquences d'entrée, sans les doublons public static IEnumerable IntersectBy(this IEnumerable source, IEnumerable other, Func keySelector, IEqualityComparer keyComparer) { source.CheckArgumentNull("source"); other.CheckArgumentNull("other"); keySelector.CheckArgumentNull("keySelector"); return source.Intersect(other, new KeyEqualityComparer(keySelector, keyComparer)); } #endregion #region SequenceEqualBy /// /// Détermine si deux séquences sont égales en comparant les éléments selon la clé spécifiée. /// /// Type des éléments de la séquence /// Type de la clé /// Première séquence /// Deuxième séquence /// Fonction qui renvoie la clé à utiliser pour comparer les éléments /// true si les deux séquences source sont de même longueur et si leurs éléments ont la même valeur pour la clé spécifiée ; sinon, false. public static bool SequenceEqualBy(this IEnumerable source, IEnumerable other, Func keySelector) { return source.SequenceEqualBy(other, keySelector, null); } /// /// Détermine si deux séquences sont égales en comparant les éléments selon la clé spécifiée, et en comparant les clés à l'aide du comparateur d'égalité spécifié. /// /// Type des éléments de la séquence /// Type de la clé /// Première séquence /// Deuxième séquence /// Fonction qui renvoie la clé à utiliser pour comparer les éléments /// Comparateur à utiliser pour comparer les clés. Si null, le comparateur par défaut sera utilisé /// true si les deux séquences source sont de même longueur et si leurs éléments ont la même valeur pour la clé spécifiée ; sinon, false. public static bool SequenceEqualBy(this IEnumerable source, IEnumerable other, Func keySelector, IEqualityComparer keyComparer) { source.CheckArgumentNull("source"); other.CheckArgumentNull("other"); keySelector.CheckArgumentNull("keySelector"); return source.SequenceEqual(other, new KeyEqualityComparer(keySelector, keyComparer)); } #endregion #region GetElementType /// /// Renvoie le type des éléments d'une séquence /// /// Type des éléments de la séquence /// Séquence dont on veut obtenir le type des éléments /// Le type des éléments de la séquence // ReSharper disable UnusedParameter.Global public static Type GetElementType(this IEnumerable source) // ReSharper restore UnusedParameter.Global { return typeof(T); } #endregion #region ElementDefaultValue /// /// Renvoie la valeur par défaut du type des éléments d'une séquence. Utile pour déclarer une variable d'un type anonyme. /// /// Type des éléments de la séquence /// Séquence /// La valeur par défaut du type des éléments de la séquence // ReSharper disable UnusedParameter.Global public static T ElementDefaultValue(IEnumerable source) // ReSharper restore UnusedParameter.Global { return default(T); } #endregion #region CommonPrefix /// /// Renvoie le préfixe commun de deux séquences, en utilisant le comparateur par défaut /// /// Type des éléments de la séquence /// Première séquence /// Deuxième séquence /// Une séquence contenant les premiers éléments de source tant qu'ils sont égaux à ceux de other public static IEnumerable CommonPrefix(this IEnumerable source, IEnumerable other) { if (source == null) throw new ArgumentNullException("source"); if (other == null) throw new ArgumentNullException("other"); return source.CommonPrefixImpl(other, null); } /// /// Renvoie le préfixe commun de deux séquences, en utilisant le comparateur spécifié /// /// Type des éléments de la séquence /// Première séquence /// Deuxième séquence /// Comparateur à utiliser pour déterminer l'égalité de deux éléments /// Une séquence contenant les premiers éléments de source tant qu'ils sont égaux à ceux de other public static IEnumerable CommonPrefix(this IEnumerable source, IEnumerable other, IEqualityComparer comparer) { if (source == null) throw new ArgumentNullException("source"); if (other == null) throw new ArgumentNullException("other"); return source.CommonPrefixImpl(other, comparer); } private static IEnumerable CommonPrefixImpl(this IEnumerable source, IEnumerable other, IEqualityComparer comparer) { comparer = comparer ?? EqualityComparer.Default; using (IEnumerator en1 = source.GetEnumerator(), en2 = other.GetEnumerator()) { while (en1.MoveNext() && en2.MoveNext()) { if (comparer.Equals(en1.Current, en2.Current)) yield return en1.Current; else yield break; } } } #endregion #region RankBy /// /// Associe à chaque élément d'une séquence son rang selon la clé spécifiée /// en ordre croissant. /// /// Type des éléments de la séquence d'entrée /// Type de la clé de tri /// Type des éléments de la séquence de sortie /// Séquence d'entrée /// Fonction qui sélectionne la clé de tri /// Fonction qui associe le rang à chaque élément /// La séquence des éléments d'entrée associés à leur rang public static IEnumerable RankBy( this IEnumerable source, Func keySelector, Func resultSelector) { return source.RankBy(keySelector, null, false, resultSelector); } /// /// Associe à chaque élément d'une séquence son rang selon la clé spécifiée /// en ordre croissant, en utilisant le comparateur spécifié. /// /// Type des éléments de la séquence d'entrée /// Type de la clé de tri /// Type des éléments de la séquence de sortie /// Séquence d'entrée /// Fonction qui sélectionne la clé de tri /// Comparateur utilisé pour comparer les valeurs de la clé de tri /// Fonction qui associe le rang à chaque élément /// La séquence des éléments d'entrée associés à leur rang public static IEnumerable RankBy( this IEnumerable source, Func keySelector, IComparer comparer, Func resultSelector) { return source.RankBy(keySelector, comparer, false, resultSelector); } /// /// Associe à chaque élément d'une séquence son rang selon la clé spécifiée /// en ordre décroissant, en utilisant le comparateur spécifié. /// /// Type des éléments de la séquence d'entrée /// Type de la clé de tri /// Type des éléments de la séquence de sortie /// Séquence d'entrée /// Fonction qui sélectionne la clé de tri /// Comparateur utilisé pour comparer les valeurs de la clé de tri /// Fonction qui associe le rang à chaque élément /// La séquence des éléments d'entrée associés à leur rang public static IEnumerable RankByDescending( this IEnumerable source, Func keySelector, IComparer comparer, Func resultSelector) { return source.RankBy(keySelector, comparer, true, resultSelector); } /// /// Associe à chaque élément d'une séquence son rang selon la clé spécifiée /// en ordre décroissant. /// /// Type des éléments de la séquence d'entrée /// Type de la clé de tri /// Type des éléments de la séquence de sortie /// Séquence d'entrée /// Fonction qui sélectionne la clé de tri /// Fonction qui associe le rang à chaque élément /// La séquence des éléments d'entrée associés à leur rang public static IEnumerable RankByDescending( this IEnumerable source, Func keySelector, Func resultSelector) { return source.RankBy(keySelector, null, true, resultSelector); } private static IEnumerable RankBy( this IEnumerable source, Func keySelector, IComparer comparer, bool descending, Func resultSelector) { comparer = comparer ?? Comparer.Default; var grouped = source.GroupBy(keySelector); var ordered = descending ? grouped.OrderByDescending(g => g.Key, comparer) : grouped.OrderBy(g => g.Key, comparer); int totalRank = 1; foreach (var group in ordered) { int rank = totalRank; foreach (var item in group) { yield return resultSelector(item, rank); totalRank++; } } } #endregion #region DenseRankBy /// /// Associe à chaque élément d'une séquence son rang, sans vide dans le classement, /// selon la clé spécifiée en ordre croissant. /// /// Type des éléments de la séquence d'entrée /// Type de la clé de tri /// Type des éléments de la séquence de sortie /// Séquence d'entrée /// Fonction qui sélectionne la clé de tri /// Fonction qui associe le rang à chaque élément /// La séquence des éléments d'entrée associés à leur rang public static IEnumerable DenseRankBy( this IEnumerable source, Func keySelector, Func resultSelector) { return source.DenseRankBy(keySelector, null, false, resultSelector); } /// /// Associe à chaque élément d'une séquence son rang, sans vide dans le classement, /// selon la clé spécifiée en ordre croissant, en utilisant le comparateur spécifié. /// /// Type des éléments de la séquence d'entrée /// Type de la clé de tri /// Type des éléments de la séquence de sortie /// Séquence d'entrée /// Fonction qui sélectionne la clé de tri /// Comparateur utilisé pour comparer les valeurs de la clé de tri /// Fonction qui associe le rang à chaque élément /// La séquence des éléments d'entrée associés à leur rang public static IEnumerable DenseRankBy( this IEnumerable source, Func keySelector, IComparer comparer, Func resultSelector) { return source.DenseRankBy(keySelector, comparer, false, resultSelector); } /// /// Associe à chaque élément d'une séquence son rang, sans vide dans le classement, /// selon la clé spécifiée en ordre décroissant, en utilisant le comparateur spécifié. /// /// Type des éléments de la séquence d'entrée /// Type de la clé de tri /// Type des éléments de la séquence de sortie /// Séquence d'entrée /// Fonction qui sélectionne la clé de tri /// Comparateur utilisé pour comparer les valeurs de la clé de tri /// Fonction qui associe le rang à chaque élément /// La séquence des éléments d'entrée associés à leur rang public static IEnumerable DenseRankByDescending( this IEnumerable source, Func keySelector, IComparer comparer, Func resultSelector) { return source.DenseRankBy(keySelector, comparer, true, resultSelector); } /// /// Associe à chaque élément d'une séquence son rang, sans vide dans le classement, /// selon la clé spécifiée en ordre décroissant. /// /// Type des éléments de la séquence d'entrée /// Type de la clé de tri /// Type des éléments de la séquence de sortie /// Séquence d'entrée /// Fonction qui sélectionne la clé de tri /// Fonction qui associe le rang à chaque élément /// La séquence des éléments d'entrée associés à leur rang public static IEnumerable DenseRankByDescending( this IEnumerable source, Func keySelector, Func resultSelector) { return source.DenseRankBy(keySelector, null, true, resultSelector); } private static IEnumerable DenseRankBy( this IEnumerable source, Func keySelector, IComparer comparer, bool descending, Func resultSelector) { comparer = comparer ?? Comparer.Default; var grouped = source.GroupBy(keySelector); var ordered = descending ? grouped.OrderByDescending(g => g.Key, comparer) : grouped.OrderBy(g => g.Key, comparer); int rank = 1; foreach (var group in ordered) { foreach (var item in group) { yield return resultSelector(item, rank); } rank++; } } #endregion #region Outer joins #region Left /// /// Effectue la jointure externe gauche entre 2 séquences. Chaque élément de la première séquence est associé /// à chaque élément correspondant de la seconde, ou à une valeur par défaut s'il n'y a pas d'élément correspondant /// dans la seconde séquence. /// /// Type des éléments de la première séquence /// Type des éléments de la seconde séquence /// Type de la clé de jointure /// Type des éléments de la séquence résultat /// Première séquence à joindre /// Seconde séquence à joindre /// Function de sélection de la clé de jointure pour la première séquence /// Function de sélection de la clé de jointure pour la seconde séquence /// Fonction de sélection du résultat à partir de deux éléments correspondants /// Le résultat de la jointure public static IEnumerable LeftOuterJoin( this IEnumerable outer, IEnumerable inner, Func outerKeySelector, Func innerKeySelector, Func resultSelector) { return outer.LeftOuterJoin(inner, outerKeySelector, innerKeySelector, resultSelector, default(TInner), null); } /// /// Effectue la jointure externe gauche entre 2 séquences. Chaque élément de la première séquence est associé /// à chaque élément correspondant de la seconde, ou à une valeur par défaut s'il n'y a pas d'élément correspondant /// dans la seconde séquence. /// /// Type des éléments de la première séquence /// Type des éléments de la seconde séquence /// Type de la clé de jointure /// Type des éléments de la séquence résultat /// Première séquence à joindre /// Seconde séquence à joindre /// Function de sélection de la clé de jointure pour la première séquence /// Function de sélection de la clé de jointure pour la seconde séquence /// Fonction de sélection du résultat à partir de deux éléments correspondants /// Valeur par défaut quand aucun élément correspondant n'existe dans la seconde séquence /// Le résultat de la jointure public static IEnumerable LeftOuterJoin( this IEnumerable outer, IEnumerable inner, Func outerKeySelector, Func innerKeySelector, Func resultSelector, TInner defaultInner) { return outer.LeftOuterJoin(inner, outerKeySelector, innerKeySelector, resultSelector, defaultInner, null); } /// /// Effectue la jointure externe gauche entre 2 séquences. Chaque élément de la première séquence est associé /// à chaque élément correspondant de la seconde, ou à une valeur par défaut s'il n'y a pas d'élément correspondant /// dans la seconde séquence. /// /// Type des éléments de la première séquence /// Type des éléments de la seconde séquence /// Type de la clé de jointure /// Type des éléments de la séquence résultat /// Première séquence à joindre /// Seconde séquence à joindre /// Function de sélection de la clé de jointure pour la première séquence /// Function de sélection de la clé de jointure pour la seconde séquence /// Fonction de sélection du résultat à partir de deux éléments correspondants /// Comparateur utilisé pour comparer les clés de jointure /// Le résultat de la jointure public static IEnumerable LeftOuterJoin( this IEnumerable outer, IEnumerable inner, Func outerKeySelector, Func innerKeySelector, Func resultSelector, IEqualityComparer comparer) { return outer.LeftOuterJoin(inner, outerKeySelector, innerKeySelector, resultSelector, default(TInner), comparer); } /// /// Effectue la jointure externe gauche entre 2 séquences. Chaque élément de la première séquence est associé /// à chaque élément correspondant de la seconde, ou à une valeur par défaut s'il n'y a pas d'élément correspondant /// dans la seconde séquence. /// /// Type des éléments de la première séquence /// Type des éléments de la seconde séquence /// Type de la clé de jointure /// Type des éléments de la séquence résultat /// Première séquence à joindre /// Seconde séquence à joindre /// Function de sélection de la clé de jointure pour la première séquence /// Function de sélection de la clé de jointure pour la seconde séquence /// Fonction de sélection du résultat à partir de deux éléments correspondants /// Valeur par défaut quand aucun élément correspondant n'existe dans la seconde séquence /// Comparateur utilisé pour comparer les clés de jointure /// Le résultat de la jointure public static IEnumerable LeftOuterJoin( this IEnumerable outer, IEnumerable inner, Func outerKeySelector, Func innerKeySelector, Func resultSelector, TInner defaultInner, IEqualityComparer comparer) { outer.CheckArgumentNull("outer"); inner.CheckArgumentNull("inner"); outerKeySelector.CheckArgumentNull("outerKeySelector"); innerKeySelector.CheckArgumentNull("innerKeySelector"); resultSelector.CheckArgumentNull("resultSelector"); return outer.GroupJoin( inner, outerKeySelector, innerKeySelector, (left, tmp) => new { left, tmp }, comparer) .SelectMany( g => g.tmp.DefaultIfEmpty(defaultInner), (g, right) => resultSelector(g.left, right)); } #endregion #region Right /// /// Effectue la jointure externe droite entre 2 séquences. Chaque élément de la seconde séquence est associé /// à chaque élément correspondant de la première, ou à une valeur par défaut s'il n'y a pas d'élément correspondant /// dans la première séquence. /// /// Type des éléments de la première séquence /// Type des éléments de la seconde séquence /// Type de la clé de jointure /// Type des éléments de la séquence résultat /// Première séquence à joindre /// Seconde séquence à joindre /// Function de sélection de la clé de jointure pour la première séquence /// Function de sélection de la clé de jointure pour la seconde séquence /// Fonction de sélection du résultat à partir de deux éléments correspondants /// Le résultat de la jointure public static IEnumerable RightOuterJoin( this IEnumerable outer, IEnumerable inner, Func outerKeySelector, Func innerKeySelector, Func resultSelector) { return outer.RightOuterJoin(inner, outerKeySelector, innerKeySelector, resultSelector, default(TOuter), null); } /// /// Effectue la jointure externe droite entre 2 séquences. Chaque élément de la seconde séquence est associé /// à chaque élément correspondant de la première, ou à une valeur par défaut s'il n'y a pas d'élément correspondant /// dans la première séquence. /// /// Type des éléments de la première séquence /// Type des éléments de la seconde séquence /// Type de la clé de jointure /// Type des éléments de la séquence résultat /// Première séquence à joindre /// Seconde séquence à joindre /// Function de sélection de la clé de jointure pour la première séquence /// Function de sélection de la clé de jointure pour la seconde séquence /// Fonction de sélection du résultat à partir de deux éléments correspondants /// Valeur par défaut quand aucun élément correspondant n'existe dans la première séquence /// Le résultat de la jointure public static IEnumerable RightOuterJoin( this IEnumerable outer, IEnumerable inner, Func outerKeySelector, Func innerKeySelector, Func resultSelector, TOuter defaultOuter) { return outer.RightOuterJoin(inner, outerKeySelector, innerKeySelector, resultSelector, defaultOuter, null); } /// /// Effectue la jointure externe droite entre 2 séquences. Chaque élément de la seconde séquence est associé /// à chaque élément correspondant de la première, ou à une valeur par défaut s'il n'y a pas d'élément correspondant /// dans la première séquence. /// /// Type des éléments de la première séquence /// Type des éléments de la seconde séquence /// Type de la clé de jointure /// Type des éléments de la séquence résultat /// Première séquence à joindre /// Seconde séquence à joindre /// Function de sélection de la clé de jointure pour la première séquence /// Function de sélection de la clé de jointure pour la seconde séquence /// Fonction de sélection du résultat à partir de deux éléments correspondants /// Comparateur utilisé pour comparer les clés de jointure /// Le résultat de la jointure public static IEnumerable RightOuterJoin( this IEnumerable outer, IEnumerable inner, Func outerKeySelector, Func innerKeySelector, Func resultSelector, IEqualityComparer comparer) { return outer.RightOuterJoin(inner, outerKeySelector, innerKeySelector, resultSelector, default(TOuter), comparer); } /// /// Effectue la jointure externe droite entre 2 séquences. Chaque élément de la seconde séquence est associé /// à chaque élément correspondant de la première, ou à une valeur par défaut s'il n'y a pas d'élément correspondant /// dans la première séquence. /// /// Type des éléments de la première séquence /// Type des éléments de la seconde séquence /// Type de la clé de jointure /// Type des éléments de la séquence résultat /// Première séquence à joindre /// Seconde séquence à joindre /// Function de sélection de la clé de jointure pour la première séquence /// Function de sélection de la clé de jointure pour la seconde séquence /// Fonction de sélection du résultat à partir de deux éléments correspondants /// Valeur par défaut quand aucun élément correspondant n'existe dans la première séquence /// Comparateur utilisé pour comparer les clés de jointure /// Le résultat de la jointure public static IEnumerable RightOuterJoin( this IEnumerable outer, IEnumerable inner, Func outerKeySelector, Func innerKeySelector, Func resultSelector, TOuter defaultOuter, IEqualityComparer comparer) { outer.CheckArgumentNull("outer"); inner.CheckArgumentNull("inner"); outerKeySelector.CheckArgumentNull("outerKeySelector"); innerKeySelector.CheckArgumentNull("innerKeySelector"); resultSelector.CheckArgumentNull("resultSelector"); return inner.LeftOuterJoin(outer, innerKeySelector, outerKeySelector, (i, o) => resultSelector(o, i), defaultOuter, comparer); } #endregion #region Full /// /// Effectue la jointure externe totale entre 2 séquences. Chaque élément de la chaque séquence est associé /// à chaque élément correspondant de l'autre, ou à une valeur par défaut s'il n'y a pas d'élément correspondant /// dans l'autre séquence. /// /// Type des éléments de la première séquence /// Type des éléments de la seconde séquence /// Type de la clé de jointure /// Type des éléments de la séquence résultat /// Première séquence à joindre /// Seconde séquence à joindre /// Function de sélection de la clé de jointure pour la première séquence /// Function de sélection de la clé de jointure pour la seconde séquence /// Fonction de sélection du résultat à partir de deux éléments correspondants /// Le résultat de la jointure public static IEnumerable FullOuterJoin( this IEnumerable left, IEnumerable right, Func leftKeySelector, Func rightKeySelector, Func resultSelector) { return left.FullOuterJoin(right, leftKeySelector, rightKeySelector, resultSelector, default(TLeft), default(TRight), null); } /// /// Effectue la jointure externe totale entre 2 séquences. Chaque élément de la chaque séquence est associé /// à chaque élément correspondant de l'autre, ou à une valeur par défaut s'il n'y a pas d'élément correspondant /// dans l'autre séquence. /// /// Type des éléments de la première séquence /// Type des éléments de la seconde séquence /// Type de la clé de jointure /// Type des éléments de la séquence résultat /// Première séquence à joindre /// Seconde séquence à joindre /// Function de sélection de la clé de jointure pour la première séquence /// Function de sélection de la clé de jointure pour la seconde séquence /// Fonction de sélection du résultat à partir de deux éléments correspondants /// Valeur par défaut quand aucun élément correspondant n'existe dans la première séquence /// Valeur par défaut quand aucun élément correspondant n'existe dans la seconde séquence /// Le résultat de la jointure public static IEnumerable FullOuterJoin( this IEnumerable left, IEnumerable right, Func leftKeySelector, Func rightKeySelector, Func resultSelector, TLeft defaultLeft, TRight defaultRight) { return left.FullOuterJoin(right, leftKeySelector, rightKeySelector, resultSelector, defaultLeft, defaultRight, null); } /// /// Effectue la jointure externe totale entre 2 séquences. Chaque élément de la chaque séquence est associé /// à chaque élément correspondant de l'autre, ou à une valeur par défaut s'il n'y a pas d'élément correspondant /// dans l'autre séquence. /// /// Type des éléments de la première séquence /// Type des éléments de la seconde séquence /// Type de la clé de jointure /// Type des éléments de la séquence résultat /// Première séquence à joindre /// Seconde séquence à joindre /// Function de sélection de la clé de jointure pour la première séquence /// Function de sélection de la clé de jointure pour la seconde séquence /// Fonction de sélection du résultat à partir de deux éléments correspondants /// Comparateur utilisé pour comparer les clés de jointure /// Le résultat de la jointure public static IEnumerable FullOuterJoin( this IEnumerable left, IEnumerable right, Func leftKeySelector, Func rightKeySelector, Func resultSelector, IEqualityComparer comparer) { return left.FullOuterJoin(right, leftKeySelector, rightKeySelector, resultSelector, default(TLeft), default(TRight), comparer); } /// /// Effectue la jointure externe totale entre 2 séquences. Chaque élément de la chaque séquence est associé /// à chaque élément correspondant de l'autre, ou à une valeur par défaut s'il n'y a pas d'élément correspondant /// dans l'autre séquence. /// /// Type des éléments de la première séquence /// Type des éléments de la seconde séquence /// Type de la clé de jointure /// Type des éléments de la séquence résultat /// Première séquence à joindre /// Seconde séquence à joindre /// Function de sélection de la clé de jointure pour la première séquence /// Function de sélection de la clé de jointure pour la seconde séquence /// Fonction de sélection du résultat à partir de deux éléments correspondants /// Valeur par défaut quand aucun élément correspondant n'existe dans la première séquence /// Valeur par défaut quand aucun élément correspondant n'existe dans la seconde séquence /// Comparateur utilisé pour comparer les clés de jointure /// Le résultat de la jointure public static IEnumerable FullOuterJoin( this IEnumerable left, IEnumerable right, Func leftKeySelector, Func rightKeySelector, Func resultSelector, TLeft defaultLeft, TRight defaultRight, IEqualityComparer comparer) { left.CheckArgumentNull("left"); right.CheckArgumentNull("right"); leftKeySelector.CheckArgumentNull("leftKeySelector"); rightKeySelector.CheckArgumentNull("rightKeySelector"); resultSelector.CheckArgumentNull("resultSelector"); var leftJoin = left.LeftOuterJoin(right, leftKeySelector, rightKeySelector, (l, r) => new { key = leftKeySelector(l), l, r }, defaultRight, comparer).Select(x => resultSelector(x.key, x.l, x.r)); var rightJoin = left.RightOuterJoin(right, leftKeySelector, rightKeySelector, (l, r) => new { key = rightKeySelector(r), l, r }, defaultLeft, comparer).Select(x => resultSelector(x.key, x.l, x.r)); return leftJoin.Union(rightJoin); } #endregion #endregion #region DistinctUntilChanged /// /// Renvoie une séquence qui contient seulement des valeurs contiguës distinctes. /// /// Type des éléments de la séquence /// Séquence d'entrée /// Une séquence qui contient seulement des valeurs contiguës distinctes public static IEnumerable DistinctUntilChanged(this IEnumerable source) { source.CheckArgumentNull("source"); return source.DistinctUntilChanged(LinqHelper.Identity, null); } /// /// Renvoie une séquence qui contient seulement des valeurs contiguës distinctes selon le comparateur spécifié. /// /// Type des éléments de la séquence /// Séquence d'entrée /// Comparateur à utiliser pour comparer les éléments. Si null, le comparateur par défaut sera utilisé /// Une séquence qui contient seulement des valeurs contiguës distinctes public static IEnumerable DistinctUntilChanged(this IEnumerable source, IEqualityComparer comparer) { source.CheckArgumentNull("source"); return source.DistinctUntilChanged(LinqHelper.Identity, comparer); } /// /// Renvoie une séquence qui contient seulement des valeurs contiguës distinctes selon le comparateur spécifié. /// /// Type des éléments de la séquence /// Type de la clé de comparaison /// Séquence d'entrée /// Fonction qui renvoie la clé à utiliser pour comparer les éléments /// Une séquence qui contient seulement des valeurs contiguës distinctes public static IEnumerable DistinctUntilChanged(this IEnumerable source, Func keySelector) { source.CheckArgumentNull("source"); keySelector.CheckArgumentNull("keySelector"); return source.DistinctUntilChanged(keySelector, null); } /// /// Renvoie une séquence qui contient seulement des valeurs contiguës distinctes selon le comparateur spécifié. /// /// Type des éléments de la séquence /// Type de la clé de comparaison /// Séquence d'entrée /// Fonction qui renvoie la clé à utiliser pour comparer les éléments /// Comparateur à utiliser pour comparer les clés. Si null, le comparateur par défaut sera utilisé /// Une séquence qui contient seulement des valeurs contiguës distinctes public static IEnumerable DistinctUntilChanged(this IEnumerable source, Func keySelector, IEqualityComparer comparer) { source.CheckArgumentNull("source"); comparer = comparer ?? EqualityComparer.Default; using (var en = source.GetEnumerator()) { if (!en.MoveNext()) yield break; yield return en.Current; TKey prevKey = keySelector(en.Current); while (en.MoveNext()) { TKey key = keySelector(en.Current); if (!comparer.Equals(prevKey, key)) { yield return en.Current; prevKey = key; } } } } #endregion } }