Conseguir uma coleção de valores de índice usando uma consulta LINQ
Pergunta
Existe uma maneira melhor de fazer isso?
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);
i. Eu estou procurando uma maneira mais eficiente ou mais elegante para obter as posições dos itens que correspondam aos critérios.
Solução
Você pode facilmente adicionar seu próprio método de extensão:
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++;
}
}
Em seguida, usá-lo com:
string[] s = {"zero", "one", "two", "three", "four", "five"};
var x = s.IndexesWhere(t => t.StartsWith("t"));
Outras dicas
Se você está apenas usando o exemplo como uma maneira de aprender LINQ, ignore este post.
Não é claro para mim que LINQ é realmente a melhor maneira de fazer isso. O código a seguir parece que seria mais eficiente desde há novas necessidades tipo anônimo a ser criado. Concedido, o seu exemplo pode ser planejado e a técnica pode ser mais útil em um contexto diferente, por exemplo, em uma estrutura de dados onde poderia tirar proveito de um índice sobre o valor, mas o código abaixo é razoavelmente simples e direta, compreensível (sem pensamento necessário) e, possivelmente, mais eficiente.
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);
}
}
Parece-me muito bem. Você pode salvar um casal caracteres, alterando a seleção para:
.Select((Value, Index) => new {Value, Index})
Há também método FindIndex na Lista da Colecção para o qual você cria um método de exclusão que pode retornar o índice da coleção. você pode consultar o seguinte link no MSDN http://msdn.microsoft.com /en-us/library/x1xzf2ca.aspx .
Como sobre isso? É semelhante ao do cartaz originais mas eu primeiro selecionar os índices e, em seguida, construir uma coleção que corresponde aos critérios.
var x = s.Select((a, i) => i).Where(i => s[i].StartsWith("t"));
Este é um pouco menos eficiente do que alguns dos outros respostas como a lista está totalmente iterada duas vezes.
Eu discuti esse problema interessante com um colega e no começo eu pensei que a solução da JonSkeet era grande, mas o meu colega apontou um problema, ou seja, que se a função é uma extensão para IEnumerable<T>
, então ele pode ser usado onde uma coleção implementa .
Com uma variedade, é seguro dizer que a ordem produzida com foreach
será respeitado (ie foreach
vai iteração do primeiro ao último), mas não seria necessariamente o caso com outras coleções (Lista, dicionário, etc.), onde foreach
não refletiria necessariamente "ordem de entrada". No entanto, a função está lá, e ele pode ser enganosa.
No final, eu acabei com algo semelhante a resposta de tvanfosson, mas como um método de extensão, para matrizes:
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();
}
Aqui está esperando List.ToArray
vai respeitar a ordem para a última operação ...