Добавить медианный метод в список - PullRequest
13 голосов
/ 11 марта 2011

Я хотел бы переопределить объект List в C #, чтобы добавить метод Median, такой как Sum или Average.Я уже нашел эту функцию:

public static decimal GetMedian(int[] array)
{
    int[] tempArray = array;
    int count = tempArray.Length;

    Array.Sort(tempArray);

    decimal medianValue = 0;

    if (count % 2 == 0)
    {
        // count is even, need to get the middle two elements, add them together, then divide by 2
        int middleElement1 = tempArray[(count / 2) - 1];
        int middleElement2 = tempArray[(count / 2)];
        medianValue = (middleElement1 + middleElement2) / 2;
    }
    else
    {
        // count is odd, simply get the middle element.
        medianValue = tempArray[(count / 2)];
    }

    return medianValue;
}

Можете ли вы сказать мне, как это сделать?

Ответы [ 8 ]

21 голосов
/ 11 марта 2011

Используйте метод расширения и создайте копию введенного массива / списка.

public static decimal GetMedian(this IEnumerable<int> source)
{
    // Create a copy of the input, and sort the copy
    int[] temp = source.ToArray();    
    Array.Sort(temp);

    int count = temp.Length;
    if (count == 0)
    {
        throw new InvalidOperationException("Empty collection");
    }
    else if (count % 2 == 0)
    {
        // count is even, average two middle elements
        int a = temp[count / 2 - 1];
        int b = temp[count / 2];
        return (a + b) / 2m;
    }
    else
    {
        // count is odd, return the middle element
        return temp[count / 2];
    }
}
16 голосов
/ 11 марта 2011

Не используйте эту функцию. Она имеет серьезные недостатки. Проверьте это:

int[] tempArray = array;     
Array.Sort(tempArray); 

Массивы ссылочные типы в C #. Сортирует указанный вами массив, а не копию. Получение медианы массива не должно изменять его порядок; возможно, он уже отсортирован в другом порядке.

Используйте Array.Copy, чтобы сначала сделать копию массива, а , а затем отсортируйте копию .

6 голосов
/ 11 марта 2011

Я бы определенно сделал эти Методы расширения :

public static class EnumerableExtensions
{
    public static decimal Median(this IEnumerable<int> list)
    {
        // Implementation goes here.
    }

    public static int Sum(this IEnumerable<int> list)
    {
        // While you could implement this, you could also use Enumerable.Sum()
    }
}

Затем вы можете использовать эти методы следующим образом:

List<int> values = new List<int>{ 1, 2, 3, 4, 5 };
var median = values.Median();

Обновление

Ох ... и, как упоминает Эрик, вы должны найти другую реализацию Медианы. Тот, который вы указали, не только изменяет исходный массив на месте, но, если я правильно его читаю, будет также возвращать целое число, а не ожидаемый десятичный.

2 голосов
/ 28 марта 2014

Вы, вероятно, не хотите использовать сортировку, чтобы найти медиану, потому что есть более эффективный способ вычислить ее иначе. Вы можете найти код для этого, который также добавляет Медиану как метод расширения для IList<T> в моем следующем ответе:

Рассчитать медиану в c #

0 голосов
/ 26 июля 2016

Я создал свое собственное решение. У меня есть большие таблицы на SQL-сервере, и .ToList () и .ToArray () не работают хорошо (вы извлекаете все строки из базы данных, пока делаете что-то еще, мне просто нужна длина записи и средние 1 или 2 строки (нечетные или четные) если кому-то интересно, у меня версия с Expression возвращает TResult вместо десятичной

   public static decimal MedianBy<T, TResult>(this IQueryable<T> sequence, Expression<Func<T, TResult>> getValue)
{
    var count = sequence.Count();
    //Use Expression bodied fuction otherwise it won't be translated to SQL query
    var list = sequence.OrderByDescending(getValue).Select(getValue);
    var mid = count / 2;
    if (mid == 0)
    {
        throw new InvalidOperationException("Empty collection");
    }
    if (count % 2 == 0)
    {
        var elem1 = list.Skip(mid - 1).FirstOrDefault();
        var elem2 = list.Skip(mid).FirstOrDefault();

        return (Convert.ToDecimal(elem1) + Convert.ToDecimal(elem2)) / 2M;
        //TODO: search for a way to devide 2 for different types (int, double, decimal, float etc) till then convert to decimal to include all posibilites
    }
    else
    {
        return Convert.ToDecimal(list.Skip(mid).FirstOrDefault());
        //ElementAt Doesn't work with SQL
        //return list.ElementAt(mid);
    }
}
0 голосов
/ 17 января 2014

Я внесу некоторые исправления в ваш метод:

замените это:

     int[] tempArray = array; 

на:

     int[] tempArray = (int[])array.Clone();
0 голосов
/ 11 марта 2011

Среднее и сумма - это методы расширения, доступные для любого IEnumerable, обеспечивающего правильную функцию преобразования в качестве параметра MSDN

decimal Median<TSource>(this IEnumerable<TSource> collection, Func<TSource,decimal> transform)
{
   var array = collection.Select(x=>transform(x)).ToArray();
   [...]
   return median;
}

transform возьмет элемент коллекции и преобразует его в десятичную (усредняемую и сопоставимую)

Я не буду погружаться здесь в детали реализации метода Median, но это не очень сложно.

Редактировать: Я видел, что вы добавили дополнительное требование вывода десятичного среднего.

PS: проверка параметров ограничена краткостью.

0 голосов
/ 11 марта 2011

Вы можете создать метод расширения для типа коллекции, которую вы хотите поддерживать.Тогда вы сможете назвать его, как если бы он был частью этого класса.

MSDN - Документация по методам расширения и примеры

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...