简体   繁体   English

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

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

I have a List with numbers, and I'd like to find the position of the minimum (not value) using LINQ我有一个带数字的列表,我想使用 LINQ 找到最小值(不是值)的 position

Example:例子:

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

Now I am looking for a function returning me现在我正在寻找一个 function 返回我

output = 2 output = 2

because the minimum is at position 2 in the list.因为列表中的最小值是 position 2。

var list = new List<int> { 3, 1, 0, 5 };
int pos = list.IndexOf(list.Min()); // returns 2

As you specifically asked for a LINQ solution, and all you got was non-LINQ solutions, here's a LINQ solution:正如您特别要求提供 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;

That however doesn't mean that LINQ is the best solution for this problem...然而,这并不意味着 LINQ 是这个问题的最佳解决方案......

Edit:编辑:

With a bit more complex code this performs a little better:使用更复杂的代码,这会表现得更好:

int index =
   values
   .Select((n, i) => new { Value = n, Index = i })
   .Aggregate((a,b) => a.Value < b.Value ? a : b)
   .Index;

To get the best performance, you would use a plain loop go get through the items, while you keep track of the lowest:为了获得最佳性能,您将使用普通循环遍历项目,同时跟踪最低的项目:

int index = 0, value = values[0];
for (int i = 1; i < values.Length; i++) {
  if (values[i] < value) {
    value = values[i];
    index = i;
  }
}

The best way to catch the position is by FindIndex This function is available only for List<>捕捉位置的最佳方式是通过FindIndex该函数仅适用于 List<>

Example例子

int id = listMyObject.FindIndex(x => x.Id == 15); 

If you have enumerator or array use this way如果您有枚举器或数组,请使用这种方式

int id = myEnumerator.ToList().FindIndex(x => x.Id == 15); 

or要么

   int id = myArray.ToList().FindIndex(x => x.Id == 15); 

I agree that LINQ isn't the best solution for this problem, but here's another variation that is O(n).我同意 LINQ 不是这个问题的最佳解决方案,但这是 O(n) 的另一个变体。 It doesn't sort and only traverses the list once.它不排序,只遍历列表一次。

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();

A list can contain multiple elements which are equal to the minimum value (see below).一个列表可以包含多个等于最小值的元素(见下文)。

The generic extension method .FindEveryIndex() I wrote works with integers, strings, ... and is quite flexible because you can specify your condition as Lambda expression.我编写的通用扩展方法.FindEveryIndex()适用于整数、字符串……并且非常灵活,因为您可以将条件指定为 Lambda 表达式。

Another advantage is that it returns a list of all indices matching the condition, not just the first element.另一个优点是它返回匹配条件的所有索引的列表,而不仅仅是第一个元素。

Regarding your question: The minimum can be returned as:关于您的问题:最小值可以返回为:

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

It will return the indices 0, 2 and 5, because the minimum in lst1 is 1 :它将返回索引 0、2 和 5,因为lst1的最小值是1

Output: 0,2,5输出:0,2,5

Example 2:示例 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]
}

Extension class:扩展类:

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;
    }
}

Note: Copy the two code snippets into a LinqPad C# program and it works instantly.注意:将两个代码片段复制到 LinqPad C# 程序中,它会立即生效。

Or, run it online with DotNetFiddle .或者,使用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);

I don't necessarily recommend this CPS-style code, but it works and is O(n), unlike the solutions that use OrderBy:我不一定推荐这种 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;

Change > to >= if you want the last minimum duplicate, not the first.如果您想要最后一个最小副本,而不是第一个,请将 > 更改为 >=。

Use .minv to get the minimum value or neither to get a 2-tuple with both the index and the minimum value.使用 .minv 获取最小值,或者两者都不使用以获取具有索引和最小值的二元组。

I can't wait for .NET to get tuples in 4.0.我等不及 .NET 在 4.0 中获得元组了。

If looking for the position of 1 or more elements of the same value in a list:如果在列表中查找 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