web-dev-qa-db-fra.com

Existe-t-il une méthode Linq pour ajouter un seul élément à un IEnumerable <T>?

Fondamentalement, j'essaie de faire quelque chose comme ça:

image.Layers

qui retourne un IEnumerable pour toutes les couches sauf la couche Parent, mais dans certains cas, je veux juste faire:

image.Layers.With(image.ParentLayer);

car il n'est utilisé qu'à quelques endroits par rapport aux 100s de l'usage habituel qui est satisfait par image.Layers. C'est pourquoi je ne veux pas créer une autre propriété qui retourne également la couche Parent.

61
Joan Venge

Une façon serait de créer une séquence singleton à partir de l'élément (tel qu'un tableau), puis de la Concat sur l'original:

image.Layers.Concat(new[] { image.ParentLayer } )

Si vous faites cela très souvent, pensez à écrire une méthode d'extension Append (ou similaire), telle que celle listée ici , qui vous permettrait de faire:

image.Layers.Append(image.ParentLayer)
51
Ani

De nombreuses implémentations ont déjà été fournies. Le mien est un peu différent (mais fonctionne tout aussi bien)

Aussi, je trouve pratique d'avoir également le contrôle sur l'ORDRE. ainsi souvent, j'ai aussi une méthode ConcatTo, mettant le nouvel élément en avant.

public static class Utility
{
    /// <summary>
    /// Adds the specified element at the end of the IEnummerable.
    /// </summary>
    /// <typeparam name="T">The type of elements the IEnumerable contans.</typeparam>
    /// <param name="target">The target.</param>
    /// <param name="item">The item to be concatenated.</param>
    /// <returns>An IEnumerable, enumerating first the items in the existing enumerable</returns>
    public static IEnumerable<T> ConcatItem<T>(this IEnumerable<T> target, T item)
    {
        if (null == target) throw new ArgumentException(nameof(target));
        foreach (T t in target) yield return t;
        yield return item;
    }

    /// <summary>
    /// Inserts the specified element at the start of the IEnumerable.
    /// </summary>
    /// <typeparam name="T">The type of elements the IEnumerable contans.</typeparam>
    /// <param name="target">The IEnummerable.</param>
    /// <param name="item">The item to be concatenated.</param>
    /// <returns>An IEnumerable, enumerating first the target elements, and then the new element.</returns>
    public static IEnumerable<T> ConcatTo<T>(this IEnumerable<T> target, T item)
    {
        if (null == target) throw new ArgumentException(nameof(target));
        yield return item;
        foreach (T t in target) yield return t;
    }
}

Ou bien, utilisez un tableau créé implicitement. (en utilisant le mot clé params) afin que vous puissiez appeler la méthode pour ajouter un ou plusieurs éléments à la fois:

public static class Utility
{
    /// <summary>
    /// Adds the specified element at the end of the IEnummerable.
    /// </summary>
    /// <typeparam name="T">The type of elements the IEnumerable contans.</typeparam>
    /// <param name="target">The target.</param>
    /// <param name="items">The items to be concatenated.</param>
    /// <returns>An IEnumerable, enumerating first the items in the existing enumerable</returns>
    public static IEnumerable<T> ConcatItems<T>(this IEnumerable<T> target, params T[] items) =>
        (target ?? throw new ArgumentException(nameof(target))).Concat(items);

    /// <summary>
    /// Inserts the specified element at the start of the IEnumerable.
    /// </summary>
    /// <typeparam name="T">The type of elements the IEnumerable contans.</typeparam>
    /// <param name="target">The IEnummerable.</param>
    /// <param name="items">The items to be concatenated.</param>
    /// <returns>An IEnumerable, enumerating first the target elements, and then the new elements.</returns>
    public static IEnumerable<T> ConcatTo<T>(this IEnumerable<T> target, params T[] items) =>
        items.Concat(target ?? throw new ArgumentException(nameof(target)));
26
realbart

Il n'y a pas de méthode unique pour ce faire. Le plus proche est le Enumerable.Concat mais qui essaie de combiner un IEnumerable<T> avec un autre IEnumerable<T>. Vous pouvez utiliser ce qui suit pour le faire fonctionner avec un seul élément

image.Layers.Concat(new [] { image.ParentLayer });

Ou ajoutez simplement une nouvelle méthode d'extension

public static IEnumerable<T> ConcatSingle<T>(this IEnumerable<T> enumerable, T value) {
  return enumerable.Concat(new [] { value });
}
12
JaredPar

Append et Prepend ont maintenant été ajoutés au framework .NET Standard, vous n'avez donc plus besoin d'écrire le vôtre. Faites simplement ceci:

image.Layers.Append(image.ParentLayer)

Voir Quelles sont les 43 API qui sont dans .Net Standard 2.0 mais pas dans .Net Framework 4.6.1? pour une grande liste de nouvelles fonctionnalités.

10
cbp

Vous pouvez utiliser Enumerable.Concat :

var allLayers = image.Layers.Concat(new[] {image.ParentLayer});
7
Reed Copsey

Vous pouvez faire quelque chose comme:

image.Layers.Concat(new[] { image.ParentLayer });

qui concatre l'énumération avec un tableau à un élément contenant la chose que vous voulez ajouter

5
thecoop

J'ai fait une fois une jolie petite fonction pour ça:

public static class CoreUtil
{    
    public static IEnumerable<T> AsEnumerable<T>(params T[] items)
    {
        return items;
    }
}

Maintenant c'est possible:

image.Layers.Append(CoreUtil.AsEnumerable(image.ParentLayer, image.AnotherLayer))
5
Gert Arnold

J'utilise les méthodes d'extension suivantes pour éviter de créer un Array inutile:

public static IEnumerable<T> ConcatSingle<T>(this IEnumerable<T> enumerable, T value) {
   return enumerable.Concat(value.Yield());
}

public static IEnumerable<T> Yield<T>(this T item) {
    yield return item;
}
3
Erwin Mayer

Si vous aimez la syntaxe de .With, écrivez-la comme méthode d'extension. IEnumerable n'en remarquera pas d'autre.

3
James Gaunt

Il existe la méthode Concat qui joint deux séquences.

1
Michael Stum
/// <summary>Concatenates elements to a sequence.</summary>
/// <typeparam name="T">The type of the elements of the input sequences.</typeparam>
/// <param name="target">The sequence to concatenate.</param>
/// <param name="items">The items to concatenate to the sequence.</param>
public static IEnumerable<T> ConcatItems<T>(this IEnumerable<T> target, params T[] items)
{
    if (items == null)
        items = new [] { default(T) };
    return target.Concat(items);
}

Cette solution est basée sur réponse de realbart . Je l'ai ajusté pour permettre l'utilisation d'une seule valeur null comme paramètre:

var newCollection = collection.ConcatItems(null)
0
Tim Pohlmann