переключаться между агрегатными функциями с помощью linq - PullRequest
2 голосов
/ 18 августа 2010

У меня есть два метода, которые выглядят почти одинаково, за исключением агрегатной функции, используемой в запросе Linq. Например:

public IEnumerable<Item> DoStuff(IEnumerable<Item> someItems) {
    var items = someItems.GroupBy(i => i.Date).Select(p => new Item(p.Key, p.Sum(r => r.Value)));
    // ...
}

public IEnumerable<Item> DoOtherStuff(IEnumerable<Item> someItems) {
    var items = someItems.GroupBy(i => i.Date).Select(p => new Item(p.Key, p.Max(r => r.Value)));
    // ...
}

где Item - это класс, подобный этому:

public class Item {
    public DateTime Date { get; private set; }
    public decimal Value { get; private set; }

    public Item(DateTime date, decimal value) {
     Date = date;
     Value = value;
    }
}

Поскольку оба метода выполняют одно и то же, я хотел бы иметь только один метод, в котором я передаю IEnumerable и агрегатную функцию (sum / max) в качестве параметров. Как мне этого добиться?

Ответы [ 3 ]

8 голосов
/ 18 августа 2010

Попробуйте следующее

public IEnumerable<Item> DoStuff(
  IEnumerable<Item> someItems,
  Func<IGrouping<DateTime,decimal>,Item> reduce) {
  var items = someItems.GroupBy(i => i.Date).Select(reduce);
  ...
}

DoStuff(someItems, p => p.Sum(r => r.Value));
DoStuff(someItems, p => p.Max(r => r.Value));
2 голосов
/ 18 августа 2010

Добавление селектора внутри затруднительно, но в конечном итоге вы можете взять Func<IEnumerable<decimal>,decimal> и передать Enumerable.Max или Enumerable.Sum в качестве экземпляра делегата. Чтобы сделать это без дублирования селектора =>r.Value, потребуется сначала выполнить селектор, то есть

// usage:
DoStuff(items, Enumerable.Sum);
DoStuff(items, Enumerable.Max);
// shared method:
public IEnumerable<Item> DoStuff(IEnumerable<Item> someItems,
        Func<IEnumerable<decimal>,decimal> aggregate)
{
    var items = someItems.GroupBy(i => i.Date).Select(
        p => new Item(p.Key, aggregate(p.Select(r=>r.Value))));
    ...
}
1 голос
/ 18 августа 2010

Хлоп:

public IEnumerable<Item> DoOtherStuff(IEnumerable<Item> someItems,
    Func<
        IGrouping<DateTime, Item>,
        Func<Func<Item, decimal>, decimal>
        > aggregateForGrouping
    )
{
    var items = someItems.GroupBy(i => i.Date)
        .Select(p => new Item(p.Key, aggregateForGrouping(p)(r => r.Value)));
    // ...
}

DoOtherStuff(someItems, p => p.Max);

Ммм, не делай этого, делай то, что сказал ДжаредПар ...

Или, если вы все еще хотите получить этот синтаксис, используйте какой-нибудь серьезный псевдоним.

using ItemGroupingByDate = IGrouping<DateTime, Item>;
using AggregateItems = Func<Func<Item, decimal>, decimal>;

public IEnumerable<Item> DoOtherStuff(
    IEnumerable<Item> someItems,
    Func<ItemGroupingByDate, AggregateItems> getAggregateForGrouping
    )
{
    var items = someItems.GroupBy(i => i.Date)
        .Select(p => new Item(p.Key, getAggregateForGrouping(p)(r => r.Value)));
    // ...
}

Это может быть почти читабельно, если вы можете использовать псевдонимы в других псевдонимах или сопоставлять пользовательских делегатов с совпадающими сигнатурами Func, тогда вы можете вставить здесь «селектор» вместо «Func».

Мы все еще спускаемся по кроличьей норе, так что это не то решение, которое вы ищете. Просто положите сюда для демонстрации:)

...