Domanda

C'è un modo migliore per farlo?

string[] s = {"zero", "one", "two", "three", "four", "five"};

var x = 
s
.Select((a,i) => new {Value = a, Index = i})
.Where(b => b.Value.StartsWith("t"))
.Select(c => c.Index);

vale a dire. Sto cercando un modo più efficiente o più elegante per ottenere le posizioni degli articoli corrispondenti ai criteri.

È stato utile?

Soluzione

Puoi facilmente aggiungere il tuo metodo di estensione:

public static IEnumerable<int> IndexesWhere<T>(this IEnumerable<T> source, Func<T, bool> predicate)
{
    int index=0;
    foreach (T element in source)
    {
        if (predicate(element))
        {
            yield return index;
        }
        index++;
    }
}

Quindi usalo con:

string[] s = {"zero", "one", "two", "three", "four", "five"};
var x = s.IndexesWhere(t => t.StartsWith("t"));

Altri suggerimenti

Se stai solo usando l'esempio come modo per imparare LINQ, ignora questo post.


Non mi è chiaro che LINQ sia in realtà il modo migliore per farlo. Il codice seguente sembra essere più efficiente poiché non è necessario creare un nuovo tipo anonimo. Certo, il tuo esempio potrebbe essere inventato e la tecnica potrebbe essere più utile in un contesto diverso, ad esempio in una struttura di dati in cui potrebbe trarre vantaggio da un indice sul valore, ma il codice seguente è ragionevolmente semplice, comprensibile (nessun pensiero richiesto) e probabilmente più efficiente.

string[] s = {"zero", "one", "two", "three", "four", "five"};
List<int> matchingIndices = new List<int>();

for (int i = 0; i < s.Length; ++i) 
{
   if (s[i].StartWith("t"))
   {
      matchingIndices.Add(i);
   }
}

Mi sembra perfetto. È possibile salvare un paio di caratteri modificando la selezione in:

.Select((Value, Index) => new {Value, Index})

Nell'elenco di raccolta è inoltre presente il metodo FindIndex per il quale si crea un metodo di eliminazione che può restituire l'indice dalla raccolta. puoi fare riferimento al seguente link in msdn http://msdn.microsoft.com /en-us/library/x1xzf2ca.aspx .

Che ne dici di questo? È simile al poster originale, ma prima seleziono gli indici e quindi costruisco una raccolta che soddisfi i criteri.

var x = s.Select((a, i) => i).Where(i => s[i].StartsWith("t"));

Questo è un po 'meno efficiente di alcune delle altre risposte poiché l'elenco viene ripetuto completamente due volte.

Ho discusso questo interessante problema con un collega e all'inizio ho pensato che la soluzione di JonSkeet fosse eccezionale, ma il mio collega ha sottolineato un problema, vale a dire che se la funzione è un'estensione di IEnumerable<T>, allora può essere usata dove una raccolta lo implementa.

Con un array, è sicuro dire che l'ordine prodotto con foreach sarà rispettato (cioè List.ToArray ripeterà dal primo all'ultimo), ma non sarebbe necessariamente il caso di altre raccolte (Elenco, Dizionario, ecc.), dove <=> non rifletterebbe necessariamente " ordine di immissione " ;. Eppure la funzione è presente e può essere fuorviante.

Alla fine, ho finito con qualcosa di simile alla risposta di tvanfosson, ma come metodo di estensione, per gli array:

public static int[] GetIndexes<T>(this T[]source, Func<T, bool> predicate)
{
    List<int> matchingIndexes = new List<int>();

    for (int i = 0; i < source.Length; ++i) 
    {
        if (predicate(source[i]))
        {
            matchingIndexes.Add(i);
        }
    }
    return matchingIndexes.ToArray();
}

Speriamo che <=> rispetti l'ordine dell'ultima operazione ...

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top