Considering that this is a very basic task, I could not think of an appropriately easy way to do it. How would you get the index of the lowest value in an int array? Using L
int[] data = new []{ 10, 2, 3, 4, 2 };
var index = data
.Select((v, i) =>new {Index = i, Val = v})
.FirstOrDefault(v => v.Val == data.Min()).Index; // 1
But be careful, because you remember that you will get an exception if the array is null
LINQ probably isn't the best solution for this problem, but here's another variation that is O(n). It doesn't sort and only traverses the array once.
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
Update: Answering the original question directly, this is how I would do it:
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
No, it doesn't use LINQ. Yes, it is more than one line. But it is really simple and really fast. Make it into a tiny little method and call it from anywhere on a single line: pos = FindMinIndex(arr);
It's ugly but it only needs a single pass through the sequence and only uses built-in framework methods:
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);
And, of course, you can write a general-purpose extension method:
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;
}
}
}
Since you mention MoreLinq, how about:
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;
Another alternative:
// Will throw if the array is empty.
// Requires two passes over the array.
int minIndex = Array.IndexOf(array, array.Min());
You could of course write your own extension-method:
// 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;
}
With some effort, you can generalize this to any type by accepting an IComparer<T>
, defaulting to Comparer<T>.Default
.
Not very memory friendly, but...
array.Select((n, i) => new { index = i, value = n })
.OrderBy(item => item.value)
.First().index