数値のリストがあり、LINQ を使用して最小値 (値ではない) の位置を見つけたい
例:
var lst = new List<int>() { 3, 1, 0, 5 };
今、私を返す関数を探しています
出力 = 2
最小値はリストの 2 番目にあるためです。
var list = new List<int> { 3, 1, 0, 5 };
int pos = list.IndexOf(list.Min()); // returns 2
具体的に 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;
}
}
位置をキャッチする最良の方法は、FindIndex
この関数は List<> でのみ使用できます。
例
int id = listMyObject.FindIndex(x => x.Id == 15);
列挙子または配列がある場合は、この方法を使用します
int id = myEnumerator.ToList().FindIndex(x => x.Id == 15);
また
int id = myArray.ToList().FindIndex(x => x.Id == 15);
LINQ がこの問題の最適な解決策ではないことに同意しますが、O(n) である別のバリエーションを次に示します。並べ替えは行わず、リストを 1 回だけトラバースします。
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
リストには、最小値に等しい複数の要素を含めることができます (以下を参照)。
私が書いた一般的な拡張メソッド.FindEveryIndex()
は、整数、文字列などで動作し、条件を Lambda 式として指定できるため、非常に柔軟です。
もう 1 つの利点は、最初の要素だけでなく、条件に一致するすべてのインデックスのリストを返すことです。
あなたの質問に関して:最小値は次のように返されます。
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
lst1
の最小値は次の1
とおりであるため、インデックス 0、2、および 5 が返されます。
出力: 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;
}
}
注: 2 つのコード スニペットを LinqPad C# プログラムにコピーすると、すぐに機能します。
または、 DotNetFiddleを使用してオンラインで実行します。
var data = new List<int> { 3, 1, 0, 5 };
var result = Enumerable.Range(0, data.Count).OrderBy(n => data[n]).First();
List<int> data = new List<int>();
data.AddRange(new[] { 3, 1, 0, 5 });
Console.WriteLine(data.IndexOf(data.Min()));
List<int>.Enumerator e = l.GetEnumerator();
int p = 0, min = int.MaxValue, pos = -1;
while (e.MoveNext())
{
if (e.Current < min)
{
min = e.Current;
pos = p;
}
++p;
}