using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using Developpez.Dotnet.Threading;
using System.Collections;
namespace Developpez.Dotnet.Collections
{
///
/// Fournit des méthodes d'extension pour les types enumérables
///
public static class EnumerableExtensions
{
#region Miscellaneous
///
/// 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 ICollection collection)
{
return (collection == null || collection.Count == 0);
}
///
/// Obtient une version synchronisée de la liste
///
/// Type générique de la liste
/// liste
/// liste synchronisée
public static IList Synchronized(this IList list)
{
return new SyncList(list);
}
///
/// Permute deux éléments d'une liste
///
/// Type des éléments de la liste
/// La liste contenant les éléments à permuter
/// L'index du premier élément
/// L'index du second élément
public static void Swap(this IList list, int index1, int index2)
{
T tmp = list[index1];
list[index1] = list[index2];
list[index2] = tmp;
}
#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 FormatAll
///
/// Renvoie une chaine contenant chaque élément de l'énumération formaté avec
/// le format spécifié
///
/// L'énumération à formater
/// Le format à appliquer à chaque élément, par exemple "x2" ou "{0:x2}"
/// Le séparateur à insérer entre chaque élément
/// La chaine contenant tous les éléments formatés
public static string FormatAll(this IEnumerable enumerable, string format, string separator)
{
if (format.IsNullOrEmpty())
{
format = "{0}";
}
else if (!(format.StartsWith("{0") && format.EndsWith("}")))
{
format = "{0:" + format + "}";
}
separator = separator ?? String.Empty;
StringBuilder sb = new StringBuilder();
foreach (var item in enumerable)
{
sb.AppendFormat(format, item);
sb.Append(separator);
}
sb.Remove(sb.Length - separator.Length, separator.Length);
return sb.ToString();
}
///
/// Renvoie une chaine contenant chaque élément de l'énumération formaté avec
/// le delegate spécifié
///
/// Type des éléments de la séquence
/// L'énumération à formater
/// Le delegate à appliquer à chaque élément pour le convertir en chaine de caractères
/// Le séparateur à insérer entre chaque élément
/// La chaine contenant tous les éléments formatés
public static string FormatAll(this IEnumerable enumerable, Func formatter, string separator)
{
if (formatter == null)
return enumerable.FormatAll((string)null, separator);
separator = separator ?? String.Empty;
StringBuilder sb = new StringBuilder();
foreach (var item in enumerable)
{
sb.Append(formatter(item));
sb.Append(separator);
}
sb.Remove(sb.Length - separator.Length, separator.Length);
return sb.ToString();
}
#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 ForEachParallel
///
/// Exécute la méthode callback de manière parallele sur tous les éléments
/// de la collection
///
/// Type de collection
/// Séquence d'éléments sur laquelle effectuer une action en parallèle
/// Méthode à rappeller sur chacun des éléments
public static void ForEachParallel(this IEnumerable enumerable, ThreadedWorker.CallBackMethod callback)
{
enumerable.ForEachParallel(callback, true);
}
///
/// Exécute la méthode callback de manière parallele sur tous les éléments
/// de la collection
///
/// Type de collection
/// Séquence d'éléments sur laquelle effectuer une action en parallèle
/// Méthode à rappeller sur chacun des éléments
/// True si l'opération doit être synchrone (attente de la fin de l'opération sur tous les éléments)
/// ou non (aucune attente, l'exécution se poursuit)
public static void ForEachParallel(this IEnumerable enumerable, ThreadedWorker.CallBackMethod callback, bool waitForCompletion)
{
enumerable.ForEachParallel(callback, waitForCompletion, -1);
}
///
/// Exécute la méthode callback de manière parallele sur tous les éléments
/// de la collection
///
/// Type de collection
/// Séquence d'éléments sur laquelle effectuer une action en parallèle
/// Méthode à rappeller sur chacun des éléments
/// True si l'opération doit être synchrone (attente de la fin de l'opération sur tous les éléments)
/// ou non (aucune attente, l'exécution se poursuit)
/// Nombre maximum de thread de travail dédié à cette tâche. Toute valeure inférieure à 1 est ignorée
public static void ForEachParallel(this IEnumerable enumerable, ThreadedWorker.CallBackMethod callback, bool waitForCompletion, int maxWorkerThreads)
{
callback.CheckArgumentNull("callback");
using (ThreadedWorker worker = new ThreadedWorker())
{
if (maxWorkerThreads > 0)
worker.MaxThreadCount = maxWorkerThreads;
worker.ForEach(enumerable, callback, waitForCompletion);
}
}
///
/// Exécute la méthode callback de manière parallele sur tous les éléments
/// de la collection
///
/// Séquence d'éléments sur laquelle effectuer une action en parallèle
/// Méthode à rappeller sur chacun des éléments
public static void ForEachParallel(this IEnumerable enumerable, ThreadedWorker.CallBackMethod callback)
{
enumerable.ForEachParallel(callback, true);
}
///
/// Exécute la méthode callback de manière parallele sur tous les éléments
/// de la collection
///
/// Séquence d'éléments sur laquelle effectuer une action en parallèle
/// Méthode à rappeller sur chacun des éléments
/// True si l'opération doit être synchrone (attente de la fin de l'opération sur tous les éléments)
/// ou non (aucune attente, l'exécution se poursuit)
public static void ForEachParallel(this IEnumerable enumerable, ThreadedWorker.CallBackMethod callback, bool waitForCompletion)
{
enumerable.ForEachParallel(callback, waitForCompletion, -1);
}
///
/// Exécute la méthode callback de manière parallele sur tous les éléments
/// de la collection
///
/// Séquence d'éléments sur laquelle effectuer une action en parallèle
/// Méthode à rappeller sur chacun des éléments
/// True si l'opération doit être synchrone (attente de la fin de l'opération sur tous les éléments)
/// ou non (aucune attente, l'exécution se poursuit)
/// Nombre maximum de thread de travail dédié à cette tâche. Toute valeure inférieure à 1 est ignorée
public static void ForEachParallel(this IEnumerable enumerable, ThreadedWorker.CallBackMethod callback, bool waitForCompletion, int maxWorkerThreads)
{
callback.CheckArgumentNull("callback");
using (ThreadedWorker worker = new ThreadedWorker())
{
if (maxWorkerThreads > 0)
worker.MaxThreadCount = maxWorkerThreads;
worker.ForEach(enumerable.Cast(), callback, waitForCompletion);
}
}
#endregion
#region Shuffle
///
/// Mélange une liste.
///
/// Type des éléments de la séquence
/// La liste à mélanger
/// Cette méthode utilise l'algorithme de Fisher–Yates (http://en.wikipedia.org/wiki/Fisher-Yates_shuffle)
///
public static void Shuffle(this IList list)
{
list.Shuffle(new Random());
}
///
/// Mélange une liste en spécifiant le générateur de nombres aléatoires à utiliser.
///
/// Type des éléments de la séquence
/// La liste à mélanger
/// Le générateur de nombres aléatoires à utiliser
/// Cette méthode utilise l'algorithme de Fisher–Yates (http://en.wikipedia.org/wiki/Fisher-Yates_shuffle)
///
public static void Shuffle(this IList list, Random rnd)
{
for (int i = list.Count - 1; i > 0; i--)
{
int swapIndex = rnd.Next(i + 1);
list.Swap(i, swapIndex);
}
}
#endregion
#region BinarySearch and related methods
///
/// Effectue une recherche binaire sur une liste triée pour trouver un élément selon
/// la valeur d'une de ses propriétés. La liste doit être triée selon cette propriété.
///
/// Type des éléments de la collection
/// Type de la clé de recherche
/// Liste dans laquelle rechercher l'élément
/// Fonction permettant d'obtenir la clé de recherche
/// Valeur de la clé recherchée
/// Le premier élément correspondant, s'il existe. Sinon, lève une InvalidOperationException.
public static T BinarySearch(this IList list, Func keySelector, TKey key)
where TKey : IComparable
{
T result;
if (list.TryBinarySearch(keySelector, key, out result))
return result;
throw new InvalidOperationException(ExceptionMessages.NoMatchingItemInList);
}
///
/// Effectue une recherche binaire sur une liste triée pour trouver un élément selon
/// la valeur d'une de ses propriétés. La liste doit être triée selon cette propriété.
/// Si aucun élément correspondant n'est trouvé, une valeur par défaut est renvoyée.
///
/// Type des éléments de la collection
/// Type de la clé de recherche
/// Liste dans laquelle rechercher l'élément
/// Fonction permettant d'obtenir la clé de recherche
/// Valeur de la clé recherchée
/// Le premier élément correspondant, s'il existe. Sinon, la valeur par défaut du type T.
public static T BinarySearchOrDefault(this IList list, Func keySelector, TKey key)
where TKey : IComparable
{
return list.BinarySearchOrDefault(keySelector, key, default(T));
}
///
/// Effectue une recherche binaire sur une liste triée pour trouver un élément selon
/// la valeur d'une de ses propriétés. La liste doit être triée selon cette propriété.
/// Si aucun élément correspondant n'est trouvé, la valeur par défaut spécifiée est renvoyée.
///
/// Type des éléments de la collection
/// Type de la clé de recherche
/// Liste dans laquelle rechercher l'élément
/// Fonction permettant d'obtenir la clé de recherche
/// Valeur de la clé recherchée
/// La valeur par défaut à renvoyer si aucun élément correspondant n'est trouvé
/// Le premier élément correspondant, s'il existe. Sinon, la valeur par défaut spécifiée.
public static T BinarySearchOrDefault(this IList list, Func keySelector, TKey key, T defaultValue)
where TKey : IComparable
{
T result;
if (list.TryBinarySearch(keySelector, key, out result))
return result;
return defaultValue;
}
///
/// Effectue une recherche binaire sur une liste triée pour tenter de trouver un élément
/// selon la valeur d'une de ses propriétés. La liste doit être triée selon cette propriété.
///
/// Type des éléments de la collection
/// Type de la clé de recherche
/// Liste dans laquelle rechercher l'élément
/// Fonction permettant d'obtenir la clé de recherche
/// Valeur de la clé recherchée
/// Paramètre de sortie qui prend la valeur du premier élément trouvé.
/// true si un élément correspondant est trouvé. Sinon, false.
public static bool TryBinarySearch(this IList list, Func keySelector, TKey key, out T result)
where TKey : IComparable
{
result = default(T);
int min = 0;
int max = list.Count;
while (min < max)
{
int mid = (max + min) / 2;
T midItem = list[mid];
TKey midKey = keySelector(midItem);
int comp = midKey.CompareTo(key);
if (comp < 0)
{
min = mid + 1;
}
else if (comp > 0)
{
max = mid - 1;
}
else
{
result = midItem;
return true;
}
}
if (min == max &&
keySelector(list[min]).CompareTo(key) == 0)
{
result = list[min];
return true;
}
return false;
}
#endregion
#region Conversion from/to CSV strings
///
/// Convertit une liste en une chaîne CSV avec le séparateur spécifié
///
/// Liste à convertir
/// Séparateur à utiliser
/// Une chaine CSV représentant les éléments de la liste
public static string ToCsvString(this IEnumerable list, string separator)
{
StringBuilder sb = new StringBuilder();
foreach (object o in list)
{
if (sb.Length > 0)
sb.Append(separator);
sb.Append(o);
}
return sb.ToString();
}
///
/// Convertit une chaine CSV en une liste d'objets du type spécifié
///
/// Type des éléments de la liste
/// Chaîne CSV à convertir
/// Séparateur à utiliser
/// Une liste contenant les éléments de la chaîne CSV
public static IEnumerable ListFromCsv(this string source, string separator)
{
List lst = new List();
string[] sItems = source.Split(new[] { separator }, StringSplitOptions.None);
foreach (string sItem in sItems)
{
T item;
if (string.IsNullOrEmpty(sItem))
{
item = default(T);
}
else
{
item = (T)Convert.ChangeType(sItem, typeof(T));
}
lst.Add(item);
}
return lst;
}
#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 SelectAggregate
///
/// Applique une fonction d'accumulation sur une séquence, en renvoyant le résultat
/// de chaque étape de l'accumulation.
///
/// Type des éléments de source
/// Type de la valeur d'accumulation
/// Séquence sur laquelle appliquer l'accumulation
/// Valeur d'accumulation initiale
/// Fonction d'accumulation à appeler sur chaque élément
/// Séquence des valeurs d'accumulation à 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 le résultat
/// de chaque étape de l'accumulation.
///
/// Type des éléments de source
/// Type de la valeur d'accumulation
/// Séquence sur laquelle appliquer l'accumulation
/// Fonction d'accumulation à appeler sur chaque élément
/// Séquence des valeurs d'accumulation à 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 le résultat
/// de chaque étape de l'accumulation. La fonction d'accumulation prend en paramètre
/// les 2 valeurs d'accumulation précédentes.
///
/// Type des éléments de source
/// Type de la valeur d'accumulation
/// Séquence sur laquelle appliquer l'accumulation
/// Valeur d'accumulation initiale (itération -1)
/// Valeur d'accumulation initiale (itération -2)
/// Fonction d'accumulation à appeler sur chaque élément
/// Séquence des valeurs d'accumulation à 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 le résultat
/// de chaque étape de l'accumulation. La fonction d'accumulation prend en paramètre
/// les 2 valeurs d'accumulation précédentes.
///
/// Type des éléments de source
/// Type de la valeur d'accumulation
/// Séquence sur laquelle appliquer l'accumulation
/// Fonction d'accumulation à appeler sur chaque élément
/// Séquence des valeurs d'accumulation à 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
///
/// 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 petite valeur pour la fonction spécifiée
public static TSource MaxBy(this IEnumerable source, Func selector)
{
return source.MaxBy(selector, null);
}
///
/// 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 petite valeur pour la fonction spécifiée
public static TSource MaxBy(this IEnumerable source, Func selector, IComparer comparer)
{
comparer = comparer ?? Comparer.Default;
TKey maxValue = default(TKey);
TSource max = default(TSource);
bool first = true;
foreach (var item in source)
{
TKey value = selector(item);
int compare = comparer.Compare(value, maxValue);
if (compare > 0 || first)
{
maxValue = value;
max = item;
}
first = false;
}
return max;
}
///
/// 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)
{
return source.MinBy(selector, null);
}
///
/// 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)
{
comparer = comparer ?? Comparer.Default;
TKey minValue = default(TKey);
TSource min = default(TSource);
bool first = true;
foreach (var item in source)
{
TKey value = selector(item);
int compare = comparer.Compare(value, minValue);
if (compare < 0 || first)
{
minValue = value;
min = item;
}
first = false;
}
return min;
}
#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)
{
comparer = comparer ?? Comparer.Default;
TSource max = default(TSource);
bool first = true;
foreach (var item in source)
{
int compare = comparer.Compare(item, max);
if (compare > 0 || first)
{
max = item;
}
first = false;
}
return max;
}
///
/// 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)
{
comparer = comparer ?? Comparer.Default;
TSource min = default(TSource);
bool first = true;
foreach (var item in source)
{
int compare = comparer.Compare(item, min);
if (compare < 0 || first)
{
min = item;
}
first = false;
}
return min;
}
#endregion
#region QuickSort
///
/// Trie la liste en utilisant l'algorithme Quicksort
///
/// Type des éléments de la liste
/// Liste à trier
public static void QuickSort(this IList list)
{
Comparison comparison = Comparer.Default.ToComparison();
QuickSort(list, comparison);
}
///
/// Trie la liste selon la clé spécifiée en utilisant l'algorithme Quicksort
///
/// Type des éléments de la liste
/// Type de la clé de tri
/// Liste à trier
/// Fonction qui renvoie la clé de tri
public static void QuickSortBy(this IList list, Func keySelector)
{
Comparison keyComparison = Comparer.Default.ToComparison();
Comparison comparison = (a, b) => keyComparison(keySelector(a), keySelector(b));
QuickSort(list, comparison);
}
///
/// Trie la liste en utilisant l'algorithme Quicksort, avec le comparateur spécifié
///
/// Type des éléments de la liste
/// Liste à trier
/// Comparateur à utiliser
public static void QuickSort(this IList list, IComparer comparer)
{
QuickSort(list, comparer.ToComparison());
}
///
/// Trie la liste en utilisant l'algorithme Quicksort, avec la fonction de comparaison spécifiée
///
/// Type des éléments de la liste
/// Liste à trier
/// Fonction de comparaison à utiliser
public static void QuickSort(this IList list, Comparison comparison)
{
QuickSort(list, 0, list.Count - 1, comparison);
}
private static void QuickSort(IList list, int left, int right, Comparison comparison)
{
if (right > left)
{
int pivot = left;
QuickSortPartition(list, left, right, ref pivot, comparison);
QuickSort(list, left, pivot - 1, comparison);
QuickSort(list, pivot + 1, right, comparison);
}
}
private static void QuickSortPartition(IList list, int left, int right, ref int pivot, Comparison comparison)
{
T pivotValue = list[pivot];
list.Swap(pivot, right);
int tmpIndex = left;
for (int i = left; i < right; i++)
{
if (comparison(list[i], pivotValue) <= 0)
{
list.Swap(i, tmpIndex);
tmpIndex++;
}
}
list.Swap(tmpIndex, right);
pivot = tmpIndex;
}
#endregion
#region 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;
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 ReplaceAt
///
/// Remplaçe 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
///
/// 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;
}
}
#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