Получение коллекции значений индекса с помощью запроса LINQ - PullRequest
15 голосов
/ 26 октября 2008

Есть ли лучший способ сделать это?

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);

т.е. Я ищу более эффективный или более элегантный способ получить позиции элементов, соответствующих критериям.

Ответы [ 6 ]

28 голосов
/ 26 октября 2008

Вы можете легко добавить свой собственный метод расширения:

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"));
6 голосов
/ 26 октября 2008

Если вы просто используете пример как способ изучения 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);
   }
}
5 голосов
/ 26 октября 2008

Кажется, хорошо для меня. Вы можете сохранить пару символов, изменив выбор на:

.Select((Value, Index) => new {Value, Index})
2 голосов
/ 16 апреля 2009

Существует также метод FindIndex в Списке коллекций, для которого вы создаете метод удаления, который может возвращать индекс из коллекции. Вы можете обратиться к следующей ссылке в MSDN http://msdn.microsoft.com/en-us/library/x1xzf2ca.aspx.

1 голос
/ 24 июня 2009

Как насчет этого? Это похоже на оригинальный постер, но я сначала выбираю индексы, а затем собираю коллекцию, которая соответствует критериям.

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

Это немного менее эффективно, чем некоторые другие ответы, так как список полностью повторяется дважды.

0 голосов
/ 20 октября 2011

Я обсуждал эту интересную проблему с коллегой, и сначала мне показалось, что решение 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 будет соблюдать порядок последней операции ...

...