Подтвердить что ты не робот

Получить индексы всех совпадающих значений из списка с помощью Linq

Эй, эксперты Linq,

Я просто задал очень похожий вопрос и знаю, что решение, вероятно, СУПЕР легко, но по-прежнему не в состоянии окунуться в голову, как сделать эту довольно простую задачу наиболее эффективным способом с помощью linq.

Мой основной сценарий заключается в том, что у меня есть список значений, например:

Lst1:
a
a
b
b
c
b
a
c
a

И я хочу создать новый список, который будет содержать все индексы из Lst1, где, скажем, value = "a". Итак, в этом примере мы имели бы:

LstIndexes:
0
1
6
8

Теперь я знаю, что могу сделать это с помощью Loops (чего я бы предпочел избежать в пользу Linq), и я даже понял, как это сделать с Linq следующим образом:

LstIndexes= Lst1.Select(Function(item As String, index As Integer) index) _
                .Where(Function(index As Integer) Lst1(index) = "a").ToList

Моя задача состоит в том, что она дважды повторяется над списком и поэтому неэффективна.

Как я могу получить свой результат наиболее эффективным способом с помощью Linq?

Спасибо!!!!

4b9b3361

Ответ 1

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

Тем не менее, ваш Select действительно просто получает последовательность всех индексов; это проще сделать с помощью Enumerable.Range:

var result = Enumerable.Range(0, lst1.Count)
             .Where(i => lst1[i] == "a")
             .ToList();

Понимание того, почему список на самом деле не повторяется дважды, немного привыкнет. Я попытаюсь дать основное объяснение.

Вы должны подумать о большинстве методов LINQ, таких как Select и Where as pipe. Каждый метод выполняет небольшую работу. В случае Select вы даете ему метод, и он по существу говорит: "Всякий раз, когда кто-то спрашивает меня о моем следующем элементе, я сначала спрошу свою входную последовательность для элемента, а затем воспользуюсь методом, который я должен преобразовать в нечто иначе, а затем дайте этот предмет тому, кто меня использует". Where, более или менее, говорит: "Всякий раз, когда кто-то спрашивает меня об элементе, я попрошу свою входную последовательность для элемента, если функция скажет, что это хорошо, я передам его, если нет, я буду продолжать спрашивать для элементов, пока я не получу тот, который пройдет."

Итак, когда вы связываете их с тем, что происходит, ToList запрашивает первый элемент, он переходит в Where, так как он для него первый элемент, Where переходит в Select и запрашивает его для первого элемента, Select идет в список, чтобы задать его первый элемент. Затем список предоставляет первый элемент. Select затем преобразует этот элемент в то, что ему нужно выплюнуть (в данном случае просто int 0) и присваивает его Where. Where принимает этот элемент и запускает его функцию, которая определяет, что она истинна и поэтому выплескивает 0 в ToList, что добавляет ее в список. Тогда все это происходит еще 9 раз. Это означает, что Select в конечном итоге будет запрашивать каждый элемент из списка ровно один раз, и он будет передавать каждый его результат непосредственно на Where, который будет кормить результаты, которые "передают тест" непосредственно в ToList, который хранит их в списке. Все методы LINQ тщательно разработаны, чтобы только когда-либо перебирать исходную последовательность (когда они повторяются один раз).

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

Ответ 2

Это работает, но, возможно, не так аккуратно.

var result = list1.Select((x, i) => new {x, i})
                  .Where(x => x.x == "a")
                  .Select(x => x.i);

Ответ 3

Как насчет этого, он работает очень хорошо для меня.

   static void Main(string[] args)
    {
        List<char> Lst1 = new List<char>();
        Lst1.Add('a'); 
        Lst1.Add('a');   
        Lst1.Add('b');   
        Lst1.Add('b');   
        Lst1.Add('c');   
        Lst1.Add('b');   
        Lst1.Add('a');   
        Lst1.Add('c');
        Lst1.Add('a');

        var result = Lst1.Select((c, i) => new { character = c, index = i })
                         .Where(list => list.character == 'a')
                         .ToList();
    }