2009-04-29 9 views
16

Ad esempio:C'è una dichiarazione di anteporre un elemento T ad un IEnumerable <T>

string element = 'a'; 
IEnumerable<string> list = new List<string>{ 'b', 'c', 'd' }; 

IEnumerable<string> singleList = ???; //singleList yields 'a', 'b', 'c', 'd' 
+0

In .NET core è built-in: '' '' public static System.Collections.Generic.IEnumerable Prepend (questo System.Collections.Generic.IEnumerable Fonte , elemento TSource); '' '' (https://docs.microsoft.com/en-us/dotnet/api/system.linq.enumerable.prepend?view=netcore-2.0) –

+0

E come parte dell'implementazione .Net Standard 2.0, è incluso in .Net 4.7.1. – NetMage

risposta

28

prendo esso è possibile non solo Insert nella lista esistente?

Bene, è possibile utilizzare new[] {element}.Concat(list).

In caso contrario, si potrebbe scrivere il proprio metodo di estensione:

public static IEnumerable<T> Prepend<T>(
      this IEnumerable<T> values, T value) { 
     yield return value; 
     foreach (T item in values) { 
      yield return item; 
     } 
    } 
    ... 

    var singleList = list.Prepend("a"); 
+6

+1. Ma :(per le parentesi di stile Java! –

+6

@Ian: Penso che tu intenda The One True Brace Style, da K & R in poi. – Richard

+0

Se non fosse Concat() invece di Union()? Union() restituisce solo elementi distinti. – Lucas

1

No, non esiste una built-in statment, dichiarazione, ma è banale da implementare tale funzione:

IEnumerable<T> PrependTo<T>(IEnumerable<T> underlyingEnumerable, params T[] values) 
{ 
    foreach(T value in values) 
     yield return value; 

    foreach(T value in underlyingEnumerable) 
     yield return value; 
} 

IEnumerable<string> singleList = PrependTo(list, element); 

Puoi persino renderlo un metodo di estensione se la versione C# lo consente.

+0

Se si rende questo un metodo di estensione, hai appena implementato IEnumerable .Union(): P – Lucas

+1

errr intendo Concat() – Lucas

5

Si può rotolare il proprio:

static IEnumerable<T> Prepend<T>(this IEnumerable<T> seq, T val) { 
yield return val; 
foreach (T t in seq) { 
    yield return t; 
} 
} 

E poi usarlo:

IEnumerable<string> singleList = list.Prepend(element); 
+2

Penso che dovrebbe essere "yield return t" all'interno del foreach e il metodo dovrebbe essere statico –

4
public static class IEnumerableExtensions 
{ 
    public static IEnumerable<T> Prepend<T>(this IEnumerable<T> ie, T item) 
    { 
     return new T[] { item }.Concat(ie); 
    } 
} 
+0

Look mi piace che tu concordi l'oggetto fino alla fine, non anteponendolo ... –

+0

Oops, buon punto. Modificherò ... – BFree

3

Questo lo farebbe ...

IEnumerable<string> singleList = new[] {element}.Concat(list); 

Se si voleva la lista singola per essere una lista quindi ...

IEnumerable<string> singleList = new List<string>() {element}.Concat(list); 

... funziona anche.

+0

Nel tuo secondo esempio, singleList è un oggetto IEnumerable, non un elenco. – Lucas

+0

risolto per Martin. – NetMage

+0

Ma è stato respinto. Gli editori non dovrebbero avere standard più elevati qui, come essere in grado di comprendere il codice? – NetMage

3

anche:

IEnumerable<string> items = Enumerable.Repeat(item, 1).Concat(list); 
1

Trovo conveniente per essere in grado di anteporre più elementi in modo chainable. Questa versione sfrutta i metodi di estensione e params.

Come nota, questa versione consente implicitamente null, ma è altrettanto semplice cambiarlo in throw new NullReferenceException() se questo è il comportamento desiderato.

public static class IEnumerableExtensions 
{ 
    public static IEnumerable<T> Prepend<T>(this IEnumerable<T> source, params T[] items) 
    { 
     return items.Concat(source ?? new T[0]); 
    } 
} 

consente una sintassi molto leggibile per i singoli elementi:

GetItems().Prepend(first, second, third); 

... e per le collezioni di elementi:

GetItems().Prepend(GetMoreItems()); 

di finitura l'esempio dei risultati della domanda in:

string element = "a"; 
IEnumerable<string> list = new List<string>{ "b", "c", "d" }; 

IEnumerable<string> singleList = list.Prepend(element); 
+1

Sembra che tu abbia un errore di battitura in cui fai riferimento a 'source' ma dichiari' sources'? – NetMage

+0

@ NetMage, grazie per averlo capito. – zzzzBov

+0

Nessun problema, questo è il mio preferito da 'Prepend' - sfortunatamente non è quello .Net Core e .Net Standard 2.0 aggiunto. – NetMage

0

Proprio come un promemoria - Elenco < T> non è l'unico tipo di contenitore. Se ti ritrovi ad aggiungere elementi in primo piano nella lista abbastanza spesso, puoi anche considerare l'utilizzo di Stack < T> per implementare il tuo contenitore.Una volta che hai uno stack

var container = new Stack<string>(new string[] { "b", "c", "d" }); 

si può sempre "anteporre" un elemento tramite

container.Push("a"); 

e ancora usare la collezione come IEnumerable < T> come in

foreach (var s in container) 
    // do sth with s 

oltre tutti gli altri metodi tipici di una pila come Pop(), Peek(), ...

Alcune delle soluzioni sopra iterate attraverso l'intero IEnumeration < T> solo per anteporre un elemento (o più di uno in un caso). Questa può essere un'operazione molto costosa se la tua collezione contiene un gran numero di elementi e la frequenza di prepending è relativamente alta.

0

Guardando alcuni esempi, penso che preferirei invertire l'estensione per applicare all'oggetto.

public static IEnumerable<T> PrependTo<T>(this T value, IEnumerable<T> values) { 
    return new[] { value }.Concat(values); 
} 

Usato come

var singleList = element.PrependTo(list); 
Problemi correlati