我有一个项目序列,希望按一个键对它们进行分组,并 for each 键计算几个聚合.

项目的数量很大,但不同键的数量很小.

玩具示例:

static List<(string Key, decimal Sum, int Count)> GroupStats(
    IEnumerable<(string Key, decimal Value)> items)
{
    return items
        .GroupBy(x => x.Key)
        .Select(g => (
            Key : g.Key,
            Sum : g.Sum(x => x.Value),
            Count : g.Count()
        ))
        .ToList();
}

使用Linq的GroupBy有一个不幸的后果,那就是需要将所有项目加载到内存中.

强制实现只会消耗与不同键的数量成比例的内存,但我想知道是否有更好的解决方案.

react 式扩展的"推"方法在理论上也应该支持低内存分组,但我没有找到一种从IObservable中逃出的方法来具体化实际值.我也对其他优雅的解决方案持开放态度(除了明显的强制性实现).

推荐答案

您可以这样做:

static IList<(string Key, decimal Sum, int Count)> GroupStats(
    IEnumerable<(string Key, decimal Value)> source)
{
    return source
        .ToObservable(Scheduler.Immediate)
        .GroupBy(x => x.Key)
        .Select(g => (
            Key: g.Key,
            Sum: g.Sum(x => x.Value).PublishLast().AutoConnect(0),
            Count: g.Count().PublishLast().AutoConnect(0)
        ))
        .ToList()
        .Wait()
        .Select(e => (e.Key, e.Sum.Wait(), e.Count.Wait()))
        .ToArray();
}
  • 使用ToObservable可以将IEnumerable<T>¹震源转换为IObservable<T>震源.这有点慢,因为默认情况下,Scheduler.CurrentThread上的订阅是scheduled,所以会传递Scheduler.Immediate.您可以进一步了解ToObservable运算符here的性能.

  • GroupByIObservable<T>转换为IObservable<IGroupedObservable<string, T>>.

  • Select将每个IGroupedObservable<string, T>转换为(string, IObservable<decimal>, IObservable<int>).PublishLast用于记住SumCount运算符发出的最后(也是唯一)值.AutoConnect(0)在emits 这些子序列时立即订阅这些子序列.

  • ToListIObservable<T>转换为IObservable<IList<T>>.完成后,外部可观察对象将发出一个列表.

  • Wait同步等待外部可观察对象完成,并发出单个列表.这就是所有工作发生的地方.在此之前,尚未枚举source序列.Wait订阅到目前为止已经构建的可观察对象,这将触发对底层可观察对象的订阅,并最终触发source的枚举.订阅期间,在当前线程上同步执行所有计算.所以动词"wait"不能准确描述这里发生的事情.

  • 接下来的Select通过等待子序列将每个(string, IObservable<decimal>, IObservable<int>)转换为(string, decimal, int).此时这些子序列已经完成,它们的单个输出存储在PublishLast中.因此,这些内部Wait调用不会触发任何严肃的工作.上一步的所有繁重工作都已完成.

  • 最后,ToArrayIEnumerable<(string, decimal, int)>转换为(string, decimal, int)的数组,这是GroupStats方法的输出.

¹我使用T作为复杂ValueTuple的占位符,这样解释就不会过于冗长

Csharp相关问答推荐

如何在Shell中创建WinUI 3图形界面?

减少Express Func T、bool断言中的公式中使用的条件运算符(4)数量(最多允许3个)

当MD5被废弃时,如何在Blazor WASM中使用它?

当打印一行x个项目时,如何打印最后一行项目?

向类注入一个工厂来创建一些资源是一个好的实践吗?

在. net毛伊岛窗口的深度链接已经创建""

我需要两个属性类吗

使用LayoutKind在C#中嵌套 struct .显式

JsonSerializer.Deserialize<;TValue>;(String,JsonSerializerOptions)何时返回空?

Thad.Sept()vs Task.Delay().Wait()

如何使用自定义负载均衡器管理Ocelot负载均衡器中的多线程和批读取

C#中Java算法的类似功能

TagHelpers在新区域不起作用

为什么方法的值在SELECT方法中不会更改?

在ASP.NET Core 8 MVC中本地化共享视图

是否可以在Entity Framework Core中使用只读 struct 作为拥有实体?

C#中类库项目的源代码生成器

使用C#代码和SQL SERVER中的相同证书签名会产生不同的结果

在Visual Studio 2022中查找Xamarin模板时遇到问题

嵌套Blazor组件内的验证