我是反应性扩展(rx)的新手,并尝试在.NET中执行以下操作(但在JS和其他语言中应相同)。
我有一个带有包含字符串和bool属性的对象的流。流将是无限的。我具有以下条件:
示例:
("one", false)--("two", true)--("three", false)--("four", false)--("five", true)--("six", true)--("seven", true)--("eight", false)--("nine", true)--("ten", false)
预期结果:
"one"--"three"--"six"--"seven"--"eight"--"ten"
请注意,已经打印了“六个”和“七个”,因为它们跟随属性设置为true的对象,即使它们自己的属性也设置为“ true”。
一个简单的.NET程序对其进行测试:
using System;
using System.Reactive.Linq;
using System.Threading;
namespace ConsoleApp1
{
class Program
{
class Result
{
public bool Flag { get; set; }
public string Text { get; set; }
}
static void Main(string[] args)
{
var source =
Observable.Create<Result>(f =>
{
f.OnNext(new Result() { Text = "one", Flag = false });
Thread.Sleep(1000);
f.OnNext(new Result() { Text = "two", Flag = true });
Thread.Sleep(1000);
f.OnNext(new Result() { Text = "three", Flag = false });
Thread.Sleep(1000);
f.OnNext(new Result() { Text = "four", Flag = false });
Thread.Sleep(1000);
f.OnNext(new Result() { Text = "five", Flag = true });
Thread.Sleep(1000);
f.OnNext(new Result() { Text = "six", Flag = true });
Thread.Sleep(1000);
f.OnNext(new Result() { Text = "seven", Flag = true });
Thread.Sleep(1000);
f.OnNext(new Result() { Text = "eight", Flag = false });
Thread.Sleep(1000);
f.OnNext(new Result() { Text = "nine", Flag = true });
Thread.Sleep(1000);
f.OnNext(new Result() { Text = "ten", Flag = false });
return () => Console.WriteLine("Observer has unsubscribed");
});
}
}
}
我尝试使用.Scan
和.Buffer
扩展名,但我不知道在我的场景中如何正确使用它们。
性能当然应该尽可能的好,因为最终流将是无限的。
答案 0 :(得分:3)
尝试这种方法:
var results = new[]
{
new Result() { Text = "one", Flag = false },
new Result() { Text = "two", Flag = true },
new Result() { Text = "three", Flag = false },
new Result() { Text = "four", Flag = false },
new Result() { Text = "five", Flag = true },
new Result() { Text = "six", Flag = true },
new Result() { Text = "seven", Flag = true },
new Result() { Text = "eight", Flag = false },
new Result() { Text = "nine", Flag = true },
new Result() { Text = "ten", Flag = false },
};
IObservable<Result> source =
Observable
.Generate(
0, x => x < results.Length, x => x + 1,
x => results[x],
x => TimeSpan.FromSeconds(1.0));
与您的source
方法相比,以上方法只是更惯用地产生了Observable.Create<Result>
。
现在是query
:
IObservable<Result> query =
source
.StartWith(new Result() { Flag = true })
.Publish(ss =>
ss
.Skip(1)
.Zip(ss, (s1, s0) =>
s0.Flag
? Observable.Return(s1)
: Observable.Empty<Result>())
.Merge());
此处使用.Publish
允许可观察的源仅具有一个订阅,但是在.Publish
方法中可以多次使用它。然后可以使用标准的Skip(1).Zip
方法来检查随后产生的值。
以下是输出:
在受到Shlomo的启发之后,这是我使用.Buffer(2, 1)
的方法:
IObservable<Result> query2 =
source
.StartWith(new Result() { Flag = true })
.Buffer(2, 1)
.Where(rs => rs.First().Flag)
.SelectMany(rs => rs.Skip(1));
答案 1 :(得分:2)
有很多方法可以做到:
var result1 = source.Publish(_source => _source
.Zip(_source.Skip(1), (older, newer) => (older, newer))
.Where(t => t.older.Flag == true)
.Select(t => t.newer)
.Merge(_source.Take(1))
.Select(r => r.Text)
);
var result2 = source.Publish(_source => _source
.Buffer(2, 1)
.Where(l => l[0].Flag == true)
.Select(l => l[1])
.Merge(_source.Take(1))
.Select(l => l.Text)
);
var result3 = source.Publish(_source => _source
.Window(2, 1)
.SelectMany(w => w
.TakeWhile((r, index) => (index == 0 && r.Flag) || index == 1)
.Skip(1)
)
.Merge(_source.Take(1))
.Select(l => l.Text)
);
var result4 = source
.Scan((result: new Result {Flag = true, Text = null}, emit: false), (state, r) => (r, state.result.Flag))
.Where(t => t.emit)
.Select(t => t.result.Text);
我偏爱“扫描一号”,但实际上取决于您。
答案 2 :(得分:0)
由于@Picci的回答,我找到了一种方法:
Func<bool, Action<Result>> printItem = print =>
{
return data => {
if(print)
{
Console.WriteLine(data.Text);
}
};
};
var printItemFunction = printItem(true);
source.Do(item => printItemFunction(item))
.Do(item => printItemFunction = printItem(item.Flag))
.Subscribe();
但是,我不确定这是否是最好的方法,因为对我而言,不使用Subscribe()而是副作用,这似乎有点奇怪。最后,我不仅要打印这些值,还要用它调用Webservice。
答案 3 :(得分:-1)
这是我在TypeScript中编码的方式
const printItem = (print: boolean) => {
return (data) => {
if (print) {
console.log(data);
}
};
}
let printItemFunction = printItem(true);
from(data)
.pipe(
tap(item => printItemFunction(item.data)),
tap(item => printItemFunction = printItem(item.printBool))
)
.subscribe()
基本思想是使用更高级别的函数printItem
,该函数返回一个知道是否打印什么内容的函数。返回的函数存储在变量printItemFunction
中。
对于源Observable发出的每个项目,首先要做的是执行printItemFunction
,传递源Observable通知的数据。
第二件事是求值printItem
函数并将结果存储在变量printItemFunction
中,以便为以下通知做好准备。
在程序开始时,printItemFunction
用true
初始化,以便始终打印第一项。
我不熟悉C#为您提供.NET的答案