查找包含最近属性值的对象的List索引

时间:2014-01-12 14:53:22

标签: c# linq list

如何找到包含最接近属性值的对象的List索引?

示例,类MyData包含属性Position。 class MyDataHandler有一个MyData列表,位置为:1,3,14,15,22。

MyDataHandler有一个名为GetClosestIndexAt的方法,如果输入值为13,则该方法必须返回索引2.

示例代码:

public class MyData
{
    public double Position { get; set; }
    public string Name { get; set; }
}

public class MyDataHandler
{
    private List<MyData> myDataList = new List<MyData>();

    public MyDataHandler()
    {
        FillMyData(myDataList);
    }

    public int GetClosestIndexAt(double position)
    {
        int index = -1;
        //How to get the index of the closest MyDataList.Position to position value.
        //index = ?????
        return index;
    }

    private void FillMyData(List<MyData> MyDataList)
    {
        //fill the data...
    }
}

3 个答案:

答案 0 :(得分:2)

您可以使用LINQ执行此操作,如下所示:

var res = myDataList
    .Select((v, i) => new {Position = v.Position, Index = i}) // Pair up the position and the index
    .OrderBy(p => Math.Abs(p.Position - position))            // Order by the distance
    .First().Index;                                           // Grab the index of the first item

这个想法是将位置与列表中的索引配对,按特定位置的距离排序,抓取第一个项目,并获得其索引。

myDataList中没有单独的元素时,您需要处理这种情况。这是demo on ideone

答案 1 :(得分:1)

使用重载的Enumerable.Select方法,通过合并元素的索引将序列的每个元素投影到新的表单中:

myDataList.Select((d,i) => new { Position = d.Position, Index = i })
          .OrderBy(x => Math.Abs(x.Position - position))
          .Select(x => x.Index)
          .DefaultIfEmpty(-1) // return -1 if there is no data in myDataList
          .First();

使用MoreLinq的MinBy运算符(可从NuGet获得)的更好解决方案:

public int GetClosestIndexAt(double position)
{
    if (!myDataList.Any())
        return -1;

    return myDataList.Select((d,i) => new { Position = d.Position, Index = i })
          .MinBy(x => Math.Abs(x.Position - position))
          .Index;
}

如果您不想使用库,可以创建自己的MinBy扩展名:

public static TSource MinBy<TSource, TKey>(
    this IEnumerable<TSource> source, Func<TSource, TKey> selector)
{
    using (IEnumerator<TSource> sourceIterator = source.GetEnumerator())
    {
        if (!sourceIterator.MoveNext())            
            throw new InvalidOperationException("Empty sequence");

        var comparer = Comparer<TKey>.Default;
        TSource min = sourceIterator.Current;
        TKey minKey = selector(min);

        while (sourceIterator.MoveNext())
        {
            TSource current = sourceIterator.Current;
            TKey currentKey = selector(current);

            if (comparer.Compare(currentKey, minKey) >= 0)
                continue;

            min = current;
            minKey = currentKey;
        }

        return min;
    }
}

答案 2 :(得分:1)

正如我在评论中所说,我认为最有效的方法是避免对整个数据进行不必要的排序,以获得第一个元素。我们可以通过搜索具有最小差异的元素来选择它,单独计算。它需要两次列表迭代但不需要排序。给出:

var myDataList = new List<MyData>()
    {
        new MyData() { Name = "Name1", Position = 1.0 },
        new MyData() { Name = "Name3", Position = 3.0 },
        new MyData() { Name = "Name14", Position = 14.0 },
        new MyData() { Name = "Name15", Position = 15.0 },
        new MyData() { Name = "Name22", Position = 22.0 },
    };
double position = 13.0;

你可以写:

var result =
    myDataList.Select((md, index) => new
    {
        Index = index,
        Diff = Math.Abs(md.Position - position)
    })
    .Where(a => a.Diff == myDataList.Min(md => Math.Abs(md.Position - position)))
    .First()
    .Index;