How can I find the List index of the object containing the closest property value?
Sample, class MyData contains a property Position. class MyDataHandler has a List of MyData and the positions are: 1, 3, 14, 15, 22.
MyDataHandler has a method called GetClosestIndexAt, If the input value is 13, the method must return index 2.
Sample code:
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...
}
}
Use overloaded Enumerable.Select method which projects each element of a sequence into a new form by incorporating the element's index:
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();
Better solution with MinBy operator of MoreLinq (available from 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;
}
You can create your own MinBy extension if you don't want to use library:
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;
}
}