Как бы вы получили индекс самого низкого значения в массиве int?

Учитывая, что это очень простая задача, я не мог придумать подходящий способ сделать это. Как бы вы получили индекс самого низкого значения в массиве int? Использование Linq/MoreLinq возможно. Пока я не нашел разумного однострочного пространства.

Ответы

Ответ 1

Поскольку вы указываете MoreLinq, как насчет:

int[] array = ..

// Will throw if the array is empty.
// If there are duplicate minimum values, the one with the smaller
// index will be chosen.
int minIndex = array.AsSmartEnumerable()
                    .MinBy(entry => entry.Value)
                    .Index;

Другая альтернатива:

// Will throw if the array is empty.
// Requires two passes over the array. 
int minIndex = Array.IndexOf(array, array.Min());

Конечно, вы можете написать свой собственный метод расширения:

// Returns last index of the value that is the minimum.
public static int IndexOfMin(this IEnumerable<int> source)
{
   if(source == null)
     throw new ArgumentNullException("source");

   int minValue = int.MaxValue;
   int minIndex = -1;
   int index = -1;

   foreach(int num in source)
   {
      index++;

      if(num <= minValue)
      {
         minValue = num;
         minIndex = index;
      }
   }

   if(index == -1)
     throw new InvalidOperationException("Sequence was empty");

   return minIndex;
}

С некоторыми усилиями вы можете обобщить это на любой тип, приняв IComparer<T>, по умолчанию Comparer<T>.Default.

Ответ 2

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

var arr = new int[] { 3, 1, 0, 5 };
int pos = Enumerable.Range(0, arr.Length)
    .Aggregate((a, b) => (arr[a] < arr[b]) ? a : b); // returns 2

Обновление: Отвечая на исходный вопрос напрямую, я бы это сделал:

var arr = new int[] { 3, 1, 0, 5 };
int pos = 0;
for (int i = 0; i < arr.Length; i++)
{
    if (arr[i] < arr[pos]) { pos = i; }
}
// pos == 2

Нет, он не использует LINQ. Да, это более чем одна строка. Но это действительно просто и очень быстро. Сделайте его крошечным методом и вызовите его из любой точки на одной строке: pos = FindMinIndex(arr);

Ответ 3

Не очень дружественный к памяти, но...

array.Select((n, i) => new { index = i, value = n })
     .OrderBy(item => item.value)
     .First().index

Ответ 4

Это некрасиво, но ему нужен только один проход через последовательность и использует только встроенные методы framework:

int index = yourArray.Select((x, i) => new { Val = x, Idx = i })
                     .Aggregate(new { Val = -1, Idx = -1 },
                                (a, x) => (x.Idx == 0 || x.Val < a.Val) ? x : a,
                                x => x.Idx);

И, конечно, вы можете написать универсальный метод расширения:

int index = yourArray.MinIndex();

// ...

public static class EnumerableExtensions
{
    public static int MinIndex<T>(
        this IEnumerable<T> source, IComparer<T> comparer = null)
    {
        if (source == null)
            throw new ArgumentNullException("source");

        if (comparer == null)
            comparer = Comparer<T>.Default;

        using (var enumerator = source.GetEnumerator())
        {
            if (!enumerator.MoveNext())
                return -1;    // or maybe throw InvalidOperationException

            int minIndex = 0;
            T minValue = enumerator.Current;

            int index = 0;
            while (enumerator.MoveNext())
            {
                index++;
                if (comparer.Compare(enumerator.Current, minValue) < 0)
                {
                    minIndex = index;
                    minValue = enumerator.Current;
                }
            }
            return minIndex;
        }
    }
}