Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C# - Find the index of nearest value from decimal array

Tags:

arrays

c#

decimal

decimal[] array = new decimal[5]{80.23,60.20,88.01,77.00,20.45};

decimal TargetNumber = 70.40;

Here, the nearest value is 77.00, How can I find the index of nearest decimal array?

Note: It should stay in the same order as I need the exact index of the nearest value. Here, Index is important than value

like image 309
Dhaval Panchal Avatar asked Mar 22 '14 05:03

Dhaval Panchal


4 Answers

int nearestIndex = Array.IndexOf(array, array.OrderBy(number => Math.Abs(number - TargetNumber)).First());         
like image 194
SpiderCode Avatar answered Nov 09 '22 05:11

SpiderCode


One nice thing about doing it the long way over using LINQ is you can stop checking early if you find a exact match. minIndex holds the index or will hold -1 if the array was empty.

decimal minDistance = 0; //0 is fine here it is never read, it is just to make the compiler happy.
int minIndex = -1;

for(int i = 0; i < array.Length; i++)
{
    var distance = Math.Abs(TargetNumber - array[i]);
    if(minIndex == -1 || distance < minDistance)
    {
        minDistance = distance;
        minIndex = i;

        //Optional, stop testing if we find a exact match.
        if(minDistance == 0)
            break;
    }
}

Just for fun I made a totally generic version, it requires you to pass in a delegate to calculate the distance factor, also it has a optional parameter to define the "minimum distance" it needs to be to stop checking for more results.

using System;
using System.Collections.Generic;

public class Program
{
    public static void Main()
    {
        decimal[] array = new decimal[5]{80.23M,80.40M,80.80M,80.00M,20.45M};
        decimal TargetNumber = 70.40M;

        var result = FindClosestIndex(TargetNumber, array, (target, element) => Math.Abs(target - element)); //Optionally add in a "(distance) => distance == 0" at the end to enable early termination.

        Console.WriteLine(result);
    }

    public static int FindClosestIndex<T,U>(T target, IEnumerable<T> elements, Func<T,T,U> distanceCalculator, Func<U, bool> earlyTermination = null) where U : IComparable<U>
    {
        U minDistance = default(U);
        int minIndex = -1;

        using(var enumerator = elements.GetEnumerator())
        for(int i = 0; enumerator.MoveNext(); i++)
        {

            var distance = distanceCalculator(enumerator.Current, target);
            if(minIndex == -1 || minDistance.CompareTo(distance) > 0)
            {
                minDistance = distance;
                minIndex = i;
            }

            if(earlyTermination != null && earlyTermination(minDistance))
                break;
        }

        return minIndex;
    }
}

Runnable example

like image 44
Scott Chamberlain Avatar answered Nov 09 '22 06:11

Scott Chamberlain


int index = array.Select((x,i) => new {Index=i, Distance = Math.Abs(TargetNumber - x)}).OrderBy(x => x.Distance).First().Index;
like image 29
Dmitriy Finozhenok Avatar answered Nov 09 '22 07:11

Dmitriy Finozhenok


You can use the Enumerable.Aggregate for this:

int nearestIndex = array
    .Select((x, i) => new { Diff = Math.Abs(x - TargetNumber), Index = i })
    .Aggregate((x, y) =>  x.Diff < y.Diff ? x : y)
    .Index;

This only doesn't require any ordering, and it only iterates through the array once.


EDIT: this version breaks out of the loop early if it finds an exact match, as @ScottChamberlein suggested:

int exactMatch = 0;
var nearestIndex = array
   .Select((x, i) => new { Diff = Math.Abs(x - TargetNumber), Index = i })
   .TakeWhile(x => x.Diff != 0M || exactMatch++ == 0)
   .Aggregate((x, y) =>  x.Diff < y.Diff ? x : y)
   .Index;
like image 30
Eren Ersönmez Avatar answered Nov 09 '22 05:11

Eren Ersönmez