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
Надеюсь я ничего не пропустил