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