Получение коллекции значений индекса с помощью запроса LINQ
Есть ли лучший способ сделать это?
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);
то есть. Я ищу более эффективный или более элегантный способ получить позиции элементов, соответствующих критериям.
Ответы
Ответ 1
Вы можете легко добавить свой собственный метод расширения:
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++;
}
}
Затем используйте его с:
string[] s = {"zero", "one", "two", "three", "four", "five"};
var x = s.IndexesWhere(t => t.StartsWith("t"));
Ответ 2
Если вы просто используете этот пример, чтобы изучить LINQ, проигнорируйте это сообщение.
Мне не ясно, что LINQ на самом деле лучший способ сделать это. Код ниже кажется, что он будет более эффективным, так как не нужно создавать новый анонимный тип. Разумеется, ваш пример может быть изобретен, и этот метод может быть более полезным в другом контексте, например, в структуре данных, где он может использовать индекс по значению, но приведенный ниже код достаточно прост, понятен (без мысли требуется) и, возможно, более эффективно.
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);
}
}
Ответ 3
Кажется, мне хорошо. Вы можете сохранить пару символов, изменив выбор:
.Select((Value, Index) => new {Value, Index})
Ответ 4
В списке Collection также есть метод FindIndex, для которого вы создаете метод удаления, который может вернуть индекс из коллекции. вы можете ссылаться на следующую ссылку в msdn http://msdn.microsoft.com/en-us/library/x1xzf2ca.aspx.
Ответ 5
Как насчет этого? Он похож на оригинальный плакат, но я сначала выбираю индексы, а затем создаю коллекцию, которая соответствует критериям.
var x = s.Select((a, i) => i).Where(i => s[i].StartsWith("t"));
Это немного менее эффективно, чем некоторые другие ответы, поскольку список полностью повторяется дважды.
Ответ 6
Я обсуждал эту интересную проблему с коллегой, и сначала я подумал, что решение JonSkeet великолепен, но мой коллега указал на одну проблему, а именно: если функция является расширением до IEnumerable<T>
, то она может быть использована там, где коллекция реализует его.
С массивом можно с уверенностью сказать, что порядок, созданный с помощью foreach
, будет соблюдаться (т.е. foreach
будет итерировать от первого до последнего), но это не обязательно будет иметь место с другими коллекциями (List, Dictionary, и т.д.), где foreach
не обязательно отражает "порядок ввода". Тем не менее функция есть, и это может ввести в заблуждение.
В конце концов, у меня получилось нечто похожее на ответ tvanfosson, но в качестве метода расширения для массивов:
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();
}
Здесь надеется List.ToArray
будет соблюдать порядок для последней операции...