Cons开发者_JS百科idering 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 Linq/MoreLinq is possible. I could not find a reasonable one-liner so far.
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
.
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);
Not very memory friendly, but...
array.Select((n, i) => new { index = i, value = n })
.OrderBy(item => item.value)
.First().index
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;
}
}
}
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
精彩评论