web-dev-qa-db-fra.com

Ajout / concaténation de deux séquences IEnumerable

J'ai deux ensembles de datarows. Ils sont chacun IEnumerable. Je veux ajouter/concaténer ces deux listes en une seule. Je suis sûr que c'est faisable. Je ne veux pas faire de boucle for et j'ai remarqué qu'il y a une méthode Union et une méthode Join sur les deux listes. Des idées?

43
MikeTWebb

En supposant que vos objets sont du même type, vous pouvez utiliser Union ou Concat. Notez que, comme le mot clé SQL UNION, l'opération Union garantira l'élimination des doublons, tandis que Concat (comme UNION ALL) Ajoutera simplement la seconde liste à la fin de la première.

IEnumerable<T> first = ...;
IEnumerable<T> second = ...;

IEnumerable<T> combined = first.Concat(second);

ou

IEnumerable<T> combined = first.Union(second);

S'ils sont de types différents, vous devrez les Select en quelque chose de commun. Par exemple:

IEnumerable<TOne> first = ...;
IEnumerable<TTwo> second = ...;

IEnumerable<T> combined = first.Select(f => ConvertToT(f)).Concat(
                          second.Select(s => ConvertToT(s)));

ConvertToT(TOne f) et ConvertToT(TTwo s) représentent une opération qui convertit en quelque sorte une instance de TOne (et TTwo, respectivement) en une instance de T.

89
Adam Robinson

Je viens de rencontrer une situation similaire où j'ai besoin de concaténer plusieurs séquences.

Recherche naturellement des solutions existantes sur Google/StackOverflow, mais n'a rien trouvé qui n'évalue pas l'énumérable, par ex. convertir en tableau puis utiliser Array.Copy() etc., j'ai donc écrit une extension et une méthode statique statique appelée ConcatMultiple.

J'espère que cela aide toute personne qui doit faire de même.

/// <summary>
/// Concatenates multiple sequences
/// </summary>
/// <typeparam name="TSource">The type of the elements of the input sequences.</typeparam>
/// <param name="first">The first sequence to concatenate.</param>
/// <param name="source">The other sequences to concatenate.</param>
/// <returns></returns>
public static IEnumerable<TSource> ConcatMultiple<TSource>(this IEnumerable<TSource> first, params IEnumerable<TSource>[] source)
{
    if (first == null)
        throw new ArgumentNullException("first");

    if (source.Any(x => (x == null)))
        throw new ArgumentNullException("source");

    return ConcatIterator<TSource>(source);
}

private static IEnumerable<TSource> ConcatIterator<TSource>(IEnumerable<TSource> first, params IEnumerable<TSource>[] source)
{
    foreach (var iteratorVariable in first)
        yield return iteratorVariable;

    foreach (var enumerable in source)
    {
        foreach (var iteratorVariable in enumerable)
            yield return iteratorVariable;
    }
}

/// <summary>
/// Concatenates multiple sequences
/// </summary>
/// <typeparam name="TSource">The type of the elements of the input sequences.</typeparam>        
/// <param name="source">The sequences to concatenate.</param>
/// <returns></returns>
public static IEnumerable<TSource> ConcatMultiple<TSource>(params IEnumerable<TSource>[] source)
{
    if (source.Any(x => (x == null)))
        throw new ArgumentNullException("source");

    return ConcatIterator<TSource>(source);
}

private static IEnumerable<TSource> ConcatIterator<TSource>(params IEnumerable<TSource>[] source)
{
    foreach (var enumerable in source)
    {
        foreach (var iteratorVariable in enumerable)
            yield return iteratorVariable;
    }
}
2
Dennis

La méthode Join est comme une jointure SQL, où la liste est référencée en fonction d'une condition, ce n'est pas une concaténation de chaîne ou un ajout à une liste. La méthode nion fait ce que vous voulez, tout comme la méthode Concat , mais les deux sont des évaluations LAZY et ont pour exigence que les paramètres soient non nuls. Ils renvoient un ConcatIterator ou un UnionIterator, et si appelé à plusieurs reprises, cela pourrait causer des problèmes . Une évaluation désirée se traduit par un comportement différent.Si c'est ce que vous voulez, une méthode d'extension comme celle ci-dessous pourrait être utilisée.

public static IEnumerable<T> myEagerConcat<T>(this IEnumerable<T> first,
                                                   IEnumerable<T> second)
{
    return (first ?? Enumerable.Empty<T>()).Concat(
           (second ?? Enumerable.Empty<T>())).ToList();
}
1
jmoreno

invocation retardée du deuxième énumérateur et des suivants

J'utilise habituellement Linq IEnumerable<T>.Concat() mais aujourd'hui, je devais être sûr à 100% que la deuxième énumération n'était pas énumérée jusqu'à ce que la première ait été traitée jusqu'à la fin. (par exemple, deux requêtes db que je ne voulais pas exécuter simultanément). La fonction suivante a donc fait l'affaire pour retarder les énumérations.

    IEnumerable<T> DelayedConcat<T>(params Func<IEnumerable<T>>[] enumerableList)
    {
        foreach(var enumerable in enumerableList)
        {
            foreach (var item in enumerable())
            {
                yield return item;
            }
        }
    }

Usage:

    return DelayedConcat(
                () => GetEnumerable1(),
                () => GetEnumerable2(),
 // and so on.. () => GetEnumerable3(),
                );

Dans cet exemple, l'appel de la fonction GetEnumerable2 sera retardé jusqu'à ce que GetEnumerable1 soit énuméré jusqu'à la fin.

0
Gerardo Grignoli