VBA/Excel RANK в C#

Я работаю над созданием вычислений из электронной таблицы в C#, и мне было интересно, если C# имеет метод, аналогичный рангу в Excel?

Ранг в Excel

Возвращает ранг числа в списке чисел. Ранг числа - это его размер относительно других значений в списке. (Если бы вы отсортировали список, ранг числа был бы его позицией.)

Синтаксис

RANK(число, порядковый, порядок)

Число - это число, ранг которого вы хотите найти.

Ref - это массив или ссылка на список чисел. Нечисловые значения в ref игнорируются.

Порядок - это число, указывающее, как ранжировать номер.

Если порядок равен 0 (ноль) или не указан, Microsoft Excel оценивает номер так, как если бы он был списком, отсортированным в порядке убывания. Если заказ имеет любое ненулевое значение, Microsoft Excel ранжирует число, как если бы ссылка была списком, отсортированным в порядке возрастания.

То же самое можно сделать с помощью кода, но я просто хотел проверить, было ли что-то, что я пропустил первым.

3 ответа

Решение

Вы можете, вроде.

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

Ранг будет восходящей позицией с нуля.

Вы можете сделать это, написав метод расширения:

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

И используйте это как:

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

... но я не уверен, что это самое разумное.

Чтобы получить эквивалент RANK, вам нужно получить минимальный индекс каждого элемента при группировании:

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

или если вы группируете по свойству класса:

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

Это работает для меня до сих пор (и это проще)

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

я использовал T поэтому он может принимать все числовые типы (int/double/decimal).

Использование аналогично Excel

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

Надеюсь я ничего не пропустил

Другие вопросы по тегам