C# 当要压缩的序列数在运行时之前未知时

C# 当要压缩的序列数在运行时之前未知时,c#,system.reactive,observable,C#,System.reactive,Observable,我需要对审批流程进行建模。在它变得相当简单之前。两个角色必须批准某些内容,然后我们可以继续下一步: public class Approved { public string ApproverRole; } var approvals = Subscribe<Approved>(); var vpOfFinance = approvals.Where(e => e.ApproverRole == "Finance VP"); var vpOfSales = appr

我需要对审批流程进行建模。在它变得相当简单之前。两个角色必须批准某些内容,然后我们可以继续下一步:

public class Approved
{
    public string ApproverRole;
}

var approvals = Subscribe<Approved>();

var vpOfFinance = approvals.Where(e => e.ApproverRole == "Finance VP");
var vpOfSales = approvals.Where(e => e.ApproverRole == "Sales VP");

var approvedByAll = vpOfFinance.Zip(vpOfSales, Tuple.Create);

approvedByAll.Subscribe(_ => SomeInterestingBusinessProcess());
公共类已批准
{
公共字符串角色;
}
var批准=订阅();
var VPOFFINCE=批准。其中(e=>e.ApproverRole==“财务副总裁”);
var vpOfSales=批准。其中(e=>e.ApproverRole==“销售副总裁”);
var approvedByAll=vpofinance.Zip(vpOfSales,Tuple.Create);
Subscribe(=>SomeInterestingBusinessProcess());
但现在有了一个新的要求:批准某件事情所需的角色数量可能会有所不同:

public class ApprovalRequested
{
    public string[] Roles;
}
var approvalRequest = Subscribe<ApprovalRequested>().Take(1);
var approvals = Subscribe<Approved>();

var approvedByAll = ???;

approvedByAll.Subscribe(_ => SomeInterestingBusinessProcess());
请求公共类批准
{
公共字符串[]角色;
}
var approvalRequest=Subscribe().Take(1);
var批准=订阅();
var approvedByAll=???;
Subscribe(=>SomeInterestingBusinessProcess());
我觉得我错过了一些很明显的东西。。。谁能给我指出正确的方向吗

编辑


澄清:审批流程是基于每个项目的。审批可以到达的顺序未定义。我们不在乎一个角色是否多次批准一个项目。

在当前版本的Rx(我从NuGet获得)中,有一个版本的
Zip()
接受一个可观察的集合并返回一个可观察的集合。有了它,您可以执行以下操作:

string[] requiredApprovals = …;

var approvedByAll = requiredApprovals
    .Select(required => approvals.Where(a => a.ApproverRole == required))
    .Zip();

approvedByAll.Subscribe(_ => SomeInterestingBusinessProcess());

但正如@Enigmativity所指出的,只有当您能够确保每个人都以相同的顺序批准,并且所有项目最终都将由所有必需的角色批准时,这才有效。如果没有,您将需要比只使用
Zip()

更复杂的东西。问题基本上可以简化为从值流中创建一个
集,其中的值可能是无序的或本质上很多的

如果N是集合的基数,我们可以简单地假设,在至少推送N种类型的值(在本例中为角色)之前,流程不会继续

下面是Zip操作符的示例解决方案;也许这可以让你开始:

    public static IObservable<IList<T>> Zip<T>(this IList<IObservable<T>> observables)
    {
        return Observable.Create<IList<T>>(observer =>
        {
            List<List<T>> store = new List<List<T>>(Enumerable.Range(1, observables.Count).Select(_ => new List<T>()));

            return new CompositeDisposable(observables.Select((o, i) => 
                o.Subscribe(value =>
                {
                    lock (store)
                    {
                        store[i].Add(value);

                        if (store.All(list => list.Count > 0))
                        {
                            observer.OnNext(store.Select(list => list[0]).ToList());
                            store.ForEach(list => list.RemoveAt(0));
                        }
                    }
                }))
            );
        });
    }

这里的一个问题是,在形成组时,您可能会丢失初始值,因此您可能希望通过将方法重写为
IObservable Zip(此IGroupedObservable-observables)

来合并初始值,
Zip
操作符希望成对的事物保持同步。你在这里做的事情可能会有财务副总裁的多个批准,而没有销售副总裁,而且事情可能会变得不同步。你需要在这里更好地定义你的需求。我找不到这个Zip重载。我在nuget上运行Rx 2.0.20823,包括Rx实验版。@JoãoBragança我添加了一个Zip方法,可以让您对集合有一个基本概念。Rx v2.0中存在以下重载:静态IObservable Zip(这是IEnumerable源代码),请参阅我对Asti答案的评论。我不知道为什么会这样。我也在使用2.0.20823,它对我来说很好。不过,我没有实验性的Rx。啊,我相信这是因为你的目标是4.5,而我仍然是4.0。你对为什么不包括它有什么见解吗?我也考虑过,但当我瞄准.Net 4.0时(从VS 2012和2010开始),它也对我有用。我需要更加关注。我需要一些可以观察的东西
        Observable.Interval(TimeSpan.FromSeconds(0.5))
                  .GroupBy(i => i % 3)
                  .Select(gr => gr.AsObservable())
                  .Buffer(3)                      
                  .SelectMany(set => set.Zip())
                  .Subscribe(v => Console.WriteLine(String.Join(",", v)));