VBA/Excel RANK in C#

一世执手 提交于 2019-12-08 03:54:33

问题


I am working on creating calculations from a spreadsheet into C#, and I was wondering if C# has a similar method to Rank in Excel?

Rank in Excel

Returns the rank of a number in a list of numbers. The rank of a number is its size relative to other values in a list. (If you were to sort the list, the rank of the number would be its position.)

Syntax

RANK(number,ref,order)

Number is the number whose rank you want to find.

Ref is an array of, or a reference to, a list of numbers. Nonnumeric values in ref are ignored.

Order is a number specifying how to rank number.

If order is 0 (zero) or omitted, Microsoft Excel ranks number as if ref were a list sorted in descending order. If order is any nonzero value, Microsoft Excel ranks number as if ref were a list sorted in ascending order.

The same can be achieved through code, but I just wanted to check if there was anything I was missing first.


回答1:


You can, sort of.

        SortedList<int, object> list = new SortedList<int, object>();
        // fill with unique ints, and then look for one
        int rank = list.Keys.IndexOf(i);

Rank will be an ascending, zero-based position.

You could pretty it up by writing an extension method:

public static class Extensions
{
    public static int Rank(this int[] array, int find)
    {
        SortedList<int, object> list = new SortedList<int, object>();
        for (int i = 0; i < array.Length; i++)
        {
            list.Add(array[i], null);
        }
        if (list.ContainsKey(find))
        {
            return list.Keys.IndexOf(find);
        }
        else
        {
            return -1;
        }
    }
}

And use it like:

    int[] ints = new int[] { 2, 7, 6, 3, 9, 12 };
    int rank = ints.Rank(2);

...but I'm not convinced its the most sensible thing to do.




回答2:


To get the equivalent of RANK you'll need to get the minimum index of each item when you group:

var ranks = list.OrderBy(x => x)
                 .Select((x, i) => new {x, i = i+1})  // get 1-based index of each item
                 .GroupBy(xi => xi.x)        // group by the item
                 .Select(g => new {rank = g.Min(xi => xi.i), items = g})  // rank = min index of group
                 .SelectMany(g => g.items, (g, gg) => new {g.rank, gg.i}) ;   // select rank and item

or if you'rs grouping by the property of a class:

var ranks = list.OrderBy(x => x.{some property})
                 .Select((x, i) => new {x, i = i+1})  // get 1-based index of each item
                 .GroupBy(xi => xi.x.{some property})        // group by the item's property
                 .Select(g => new {rank = g.Min(xi => xi.i), items = g})  // rank = min index of group
                 .SelectMany(g => g.items, (g, gg) => new {g.rank, gg.i}) ;   // select rank and item



回答3:


This works for me so far (and it is simpler)

public static int Rank<T>(T value, IEnumerable<T> data)
{
    return data.OrderByDescending(x => x).ToList().IndexOf(value) + 1;
}

I used T so it can take all numeric types (int/double/decimal).

The usage is similar to Excel

int[] data = new[] { 3, 2, 2, 3, 4 };
int rank = Rank(3, data); // returns 2

I hope I didn't miss anything



来源:https://stackoverflow.com/questions/21337900/vba-excel-rank-in-c-sharp

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!