Получить позицию элемента List ‹› в c # с помощью LINQ

У меня есть список с числами, и я хотел бы найти позицию минимума (не значения) с помощью LINQ

Пример:

var lst = new List<int>() { 3, 1, 0, 5 };

Сейчас ищу функцию, возвращающую меня

output = 2

потому что минимум находится на позиции 2 в списке.


person Community    schedule 10.07.2009    source источник
comment
всегда ли ценности будут разными?   -  person Amir    schedule 10.07.2009
comment
нет, значения не уникальны   -  person    schedule 10.07.2009
comment
Хммм, если значения не уникальны, то я полагаю, что кто-то может захотеть найти несколько позиций. Или положение первого обнаруженного минимального значения.   -  person DavidRR    schedule 18.12.2013
comment
Если вместо этого вам нужен набор уникальных значений, вам следует рассмотреть возможность использования класса коллекции, который наследуется от ISet ‹T›: то есть HashSet ‹T› или SortedSet ‹T›.   -  person DavidRR    schedule 18.12.2013


Ответы (7)


Поскольку вы специально запросили решение LINQ, а все, что у вас было, это решения, отличные от LINQ, вот решение LINQ:

List<int> values = new List<int> { 3, 1, 0, 5 };

int index =
   values
   .Select((n, i) => new { Value = n, Index = i })
   .OrderBy(n=>n.Value)
   .First()
   .Index;

Однако это не означает, что LINQ - лучшее решение этой проблемы ...

Редактировать:

С немного более сложным кодом это работает немного лучше:

int index =
   values
   .Select((n, i) => new { Value = n, Index = i })
   .Aggregate((a,b) => a.Value < b.Value ? a : b)
   .Index;

Чтобы получить максимальную производительность, вы должны использовать простой цикл, проходящий по элементам, в то время как вы отслеживаете самые низкие:

int index = 0, value = values[0];
for (int i = 1; i < values.Length; i++) {
  if (values[i] < value) {
    value = values[i];
    index = i;
  }
}
person Guffa    schedule 10.07.2009
comment
Технически кто-то еще добавил требование LINQ - person John Rasch; 10.07.2009
comment
На самом деле, похоже, что исходный постер пометил его как LINQ, но не упомянул LINQ в сообщении. - person Don Kirkby; 17.07.2009
comment
Почему голос против? Если вы не объясните, что вы считаете неправильным, это не улучшит ответ. - person Guffa; 17.07.2012
comment
@JulienGuertault: у вас есть лучшее предложение, как это сделать с помощью LINQ? - person Guffa; 20.05.2014
comment
@Guffa: вы можете использовать агрегат (например, ответ Шельцера) или вернуться к написанию метода расширения. Ваше решение работает и читается, но оно выполняет гораздо больше работы, чем необходимо: что, если мы говорим о миллионе элементов? - person Julien Guertault; 23.05.2014
comment
@JulienGuertault: Справедливый вопрос. Я добавил альтернативу с лучшей производительностью. Теперь проголосуйте против принятого ответа, который выполняет гораздо больше работы, чем должен ...;) - person Guffa; 23.05.2014
comment
Кажется, это лучшее решение, чем принятый ответ ... - person Steven.Xi; 10.12.2020

Лучше всего поймать позицию по FindIndex Эта функция доступна только для List ‹>

Пример

int id = listMyObject.FindIndex(x => x.Id == 15); 

Если у вас есть перечислитель или массив, используйте этот способ

int id = myEnumerator.ToList().FindIndex(x => x.Id == 15); 

or

   int id = myArray.ToList().FindIndex(x => x.Id == 15); 
person daniele3004    schedule 05.09.2016

Я согласен с тем, что LINQ - не лучшее решение этой проблемы, но вот еще один вариант - O (n). Он не сортирует, а только обходит список один раз.

var list = new List<int> { 3, 1, 0, 5 };
int pos = Enumerable.Range(0, list.Count)
    .Aggregate((a, b) => (list[a] < list[b]) ? a : b); // returns 2
person shoelzer    schedule 13.12.2012

Список может содержать несколько элементов, равных минимальному значению (см. Ниже).

Написанный мною общий метод расширения .FindEveryIndex() работает с целыми числами, строками и т. Д. И является довольно гибким, поскольку вы можете указать свое условие как лямбда-выражение.

Еще одно преимущество состоит в том, что он возвращает список всех индексов, соответствующих условию, а не только первый элемент.

Относительно вашего вопроса: Минимальная сумма может быть возвращена как:

var lst = new List<int>() { 1, 2, 1, 3, 4, 1 };  // example list
var minimum = lst.Min();  // get the minumum value of lst
var idx = lst.FindEveryIndex(x => x == minimum);  // finds all indices matching condition
Console.WriteLine($"Output: {String.Join(',', idx.ToArray())}");  // show list of indices

Он вернет индексы 0, 2 и 5, потому что минимум в lst1 равен 1:

Выход: 0,2,5

Пример 2:

void Main()
{   
    // working with list of integers
    var lst1 = new List<int>() { 1, 2, 1, 3, 4, 1 };
    lst1.FindEveryIndex(x => x==1).Dump("Find 1");   // finds indices: [0, 2, 5]
    lst1.FindEveryIndex(x => x==2).Dump("Find 2");   // finds index: [1]
    lst1.FindEveryIndex(x => x==9).Dump("Find 9");   // returns [-1]

    // working with list of strings
    var lst2 = new List<string>() { "A", "B", "A", "C", "D", "A"};
    lst2.FindEveryIndex(x => x=="A").Dump("Find A");   // finds indices: [0, 2, 5]
    lst2.FindEveryIndex(x => x=="B").Dump("Find B");   // finds index: [1]
    lst2.FindEveryIndex(x => x=="X").Dump("Find X");   // returns [-1]
}

Класс расширения:

public static class Extension
{
    // using System.Collections.Generic;
    public static IEnumerable<int> FindEveryIndex<T>(this IEnumerable<T> items, 
                                                     Predicate<T> predicate)
    {
        int index = 0; bool found = false;
        foreach (var item in items)
        {
            if (predicate(item))
            {
                found = true; yield return index;
            };
            index++;
        }
        if (!found) yield return -1;
    }
}

Примечание. Скопируйте два фрагмента кода в программу LinqPad C #, и она сразу же начнет работать.

Или запустите его в Интернете с помощью DotNetFiddle.

person Matt    schedule 03.12.2020

Я не обязательно рекомендую этот код в стиле CPS, но он работает и имеет значение O (n), в отличие от решений, использующих OrderBy:

var minIndex = list.Aggregate(
    new { i = 0, mini = -1, minv = int.MaxValue },
    (min, x) => (min.minv > x)
        ? new { i = min.i + 1, mini = min.i, minv = x }
        : new { i = min.i + 1, mini = min.mini, minv = min.minv })
    .mini;

Измените> на> =, если вам нужен последний минимум дубликата, а не первый.

Используйте .minv для получения минимального значения или ни одного, чтобы получить 2-кортеж с индексом и минимальным значением.

Я не могу дождаться, когда .NET получит кортежи в 4.0.

person Joe Chung    schedule 11.07.2009

person    schedule
comment
Что произойдет, если в списке есть еще один элемент, тоже 0? Я не критикую, я просто хочу знать. - person Matthew Groves; 10.07.2009
comment
отслеживают ли общие объекты списка минимальное значение в них? - person maxwellb; 10.07.2009
comment
@mgroves - возвращает первый индекс минимума - person John Rasch; 10.07.2009
comment
Я думаю, что IndexOf возвращает первый индекс указанного параметра или -1, если не найден. - person maxwellb; 10.07.2009
comment
(IndexOf) Выполняет поиск указанного объекта и возвращает отсчитываемый от нуля индекс первого вхождения во всем списке ‹(Of‹ (T ›)›). msdn.microsoft.com/en-us/library/e4w08k17.aspx - person Greg; 10.07.2009
comment
Это выбрасывается, если вы даете ему пустой список. Может это желательно? - person l33t; 03.12.2020

person    schedule
comment
Думаю, этот ответ был юмористическим :) - person Softlion; 27.10.2011