簡體   English   中英

使用 LINQ 在 c# 中獲取 List<> 元素 position

[英]Get List<> element position in c# using LINQ

我有一個帶數字的列表,我想使用 LINQ 找到最小值(不是值)的 position

例子:

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

現在我正在尋找一個 function 返回我

output = 2

因為列表中的最小值是 position 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) 的另一個變體。 它不排序,只遍歷列表一次。

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
var data = new List<int> { 3, 1, 0, 5 };

var result = Enumerable.Range(0, data.Count).OrderBy(n => data[n]).First();

一個列表可以包含多個等於最小值的元素(見下文)。

我編寫的通用擴展方法.FindEveryIndex()適用於整數、字符串……並且非常靈活,因為您可以將條件指定為 Lambda 表達式。

另一個優點是它返回匹配條件的所有索引的列表,而不僅僅是第一個元素。

關於您的問題:最小值可以返回為:

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在線運行它。

List<int> data = new List<int>();
data.AddRange(new[] { 3, 1, 0, 5 });
Console.WriteLine(data.IndexOf(data.Min()));
int min = 0;
bool minIsSet = false;

var result = ints
  .Select( (x, i) => new {x, i}
  .OrderBy(z => z.x)
  .Select(z => 
  {
    if (!minIsSet)
    {
      min = z.x;
      minIsSet = true;
    }
    return z;
  }
  .TakeWhile(z => z.x == min)
  .Select(z => z.i);

我不一定推薦這種 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 獲取最小值,或者兩者都不使用以獲取具有索引和最小值的二元組。

我等不及 .NET 在 4.0 中獲得元組了。

如果在列表中查找 1 個或多個具有相同值的元素的 position:

using System.Linq;
List<int> lst = new List<int>() { 3, 1, 0, 5, 0 }; // works for int array as well

List<int> ids = lst.Select((value, index) => new { value, index })
                     .Where(x => x.value == 0)
                     .Select(x => x.index)
                     .ToList(); // returns 2, 4
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;
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM