I have a hot observable that emits at random intervals like different numbers.
1--1-----1--1-2--4
I am looking for a way when there are duplicates within a predefined interval to take this item and merge it back to the sequence until it finds space to bypass the interval threshold.
I have implemented a solution which I believe is not optimum as when I test it in production with real objects instead of Integers it creates kind of back-pressure in the system and I see the CPU going mad. Below is the test of what I have up to now.
using System;
using System.Collections.Generic;
using System.Reactive.Disposables;
using System.Reactive.Linq;
using System.Reactive.Subjects;
using Microsoft.Reactive.Testing;
using Xunit;
using Xunit.Abstractions;
namespace Specs{
public class CollectDuplicatesSpecs:ReactiveTest{
private readonly ITestOutputHelper _outputHelper;
public CollectDuplicatesSpecs(ITestOutputHelper outputHelper){
_outputHelper = outputHelper;
}
[Fact]
public void MethodName(){
var testScheduler = new TestScheduler();
var hotObservable = testScheduler.CreateHotObservable(OnNext(10, 1), OnNext(20, 1), OnNext(30, 1),OnNext(40, 1));
var subject = new Subject<int>();
hotObservable.Merge(subject).Window(TimeSpan.FromTicks(20), testScheduler).Select(observable => {
observable.CollectDuplicates(i => i).Delay(TimeSpan.FromTicks(1), testScheduler).Subscribe(subject);
return observable.Distinct();
}).SelectMany(observable => observable).Subscribe(i => _outputHelper.WriteLine($"{testScheduler.Clock}-{i}"));
testScheduler.AdvanceBy(160);
}
}
public static class RxEx{
public static IObservable<TSource> CollectDuplicates<TSource>(this IObservable<TSource> source, Func<TSource, int> keySelector = null) {
return Observable.Create<TSource>(observer => {
var dubplicateCollector = new DubplicateCollector<TSource>(keySelector);
var duplicateCollectorSubscription = dubplicateCollector.Matches.Subscribe(observer);
var disposable = source.Distinct(dubplicateCollector).Finally(dubplicateCollector.Dispose).Subscribe();
return new CompositeDisposable(disposable, duplicateCollectorSubscription, dubplicateCollector);
});
}
}
public class DubplicateCollector<TSource> : IEqualityComparer<TSource>,IDisposable {
private readonly Func<TSource, int> _keySelector;
readonly Subject<TSource> _matches = new Subject<TSource>();
public DubplicateCollector(Func<TSource, int> keySelector) {
_keySelector = keySelector;
}
public IObservable<TSource> Matches => _matches;
public bool Equals(TSource x, TSource y) {
var equals = IsMatch(x, y);
if (equals)
_matches.OnNext(x);
return equals;
}
private bool IsMatch(TSource x, TSource y) {
if (_keySelector != null)
return _keySelector(x).Equals(_keySelector(y));
var equals = x != null && x.Equals(y);
return equals;
}
public int GetHashCode(TSource obj) {
return _keySelector(obj);
}
public void Dispose(){
_matches?.Dispose();
}
}
}
which prints
10-1
21-1
40-1
60-1
I'm struggling to get what you want: Some marble diagrams may help. I'm assuming you essentially want something like a smoothing operator: If messages come in bursts, then smooth them out over time somehow.
Based on this answer, you can create an operator that handles the smoothing:
public static class ObservableDrainExtensions
{
public static IObservable<T> TimeDrained<T>(this IObservable<T> source, TimeSpan ts, IScheduler scheduler)
{
return source.Drain(x => Observable.Empty<T>().Delay(ts, scheduler).StartWith(x));
}
public static IObservable<T> TimeDrained<T>(this IObservable<T> source, TimeSpan ts)
{
return TimeDrained(source, ts, Scheduler.Default);
}
public static IObservable<TOut> Drain<TSource, TOut>(this IObservable<TSource> source,
Func<TSource, IObservable<TOut>> selector)
{
return Observable.Defer(() =>
{
BehaviorSubject<Unit> queue = new BehaviorSubject<Unit>(new Unit());
return source
.Zip(queue, (v, q) => v)
.SelectMany(v => selector(v)
.Do(_ => { }, () => queue.OnNext(new Unit()))
);
});
}
}
Drain can linearly smooth things out, TimeDrained does so based on a TimeSpan. You can combine this with GroupBy to add the distinct element to it:
[Fact]
public void MethodName()
{
var testScheduler = new TestScheduler();
var hotObservable = testScheduler.CreateHotObservable(
OnNext(10, 1),
OnNext(20, 1),
OnNext(30, 1),
OnNext(40, 1)
);
var ts = TimeSpan.FromTicks(20);
hotObservable
.GroupBy(i => i) //comparison key
.Select(g => g.TimeDrained(ts, testScheduler))
.Merge()
.Subscribe(i => Console.WriteLine($"{testScheduler.Clock}-{i}"));
testScheduler.AdvanceBy(160);
}
Output is:
10-1
30-1
50-1
70-1
If this isn't what you're looking for, then please clear up the question.
Related
Could someone provide me a small example on how to Use the .NET 6 LINQ IntersectBy and ExceptBy methods? MSDN hasn't got any examples and the one I tried doesn't compile due to CS0411 error. The example I tried:
namespace Test
{
internal struct Example
{
public int X { get; set; }
public int Y { get; set; }
public override string ToString()
{
return $"{X}, {Y}";
}
}
public class Program
{
public static void Main()
{
var elements = new List<Example>
{
new Example { X = 10, Y = 20 },
new Example { X = 11, Y = 23 },
};
var elements2 = new List<Example>
{
new Example { X = 10, Y = 12 },
new Example { X = 44, Y = 20 },
};
//ok
var union = elements.UnionBy(elements2, x => x.X);
//CS0411 - Why ?
var intersect = elements.IntersectBy(elements2, x => x.X);
//CS0411 - Why ?
var except = elements.ExceptBy(elements2, x => x.X);
Console.ReadKey();
}
}
}
Granted the documentation doesn't have any examples, it states that the selector function should select TKey i.e. the type of the second collection. The following should work:
var intersect = elements.IntersectBy(elements2, x => x);
var except = elements.ExceptBy(elements2, x => x);
Although I think this may be closer to what you want:
var intersect = elements.IntersectBy(elements2.Select(e => e.X), x => x.X);
For more complex types, you may want to consider implementing an IEqualityComparer and using the overloads that take one as an argument.
I made my own ExceptByProperty method like this
Usage:
var new = items.ExceptByProperty(duplicateItems, x => x.Id).ToList();
Code:
public static class EnumerableExtensions
{
public static IEnumerable<TSource> ExceptByProperty<TSource, TProperty>(this IEnumerable<TSource> first, IEnumerable<TSource> second, Func<TSource, TProperty> keySelector)
{
return first.ExceptBy(second, x => x, GenericComparer<TSource, TProperty>.Comparer(keySelector));
}
}
public sealed class GenericComparer<T, TProperty> : IEqualityComparer<T>
{
public static IEqualityComparer<T> Comparer(Func<T, TProperty> selector)
{
return new GenericComparer<T, TProperty>(selector);
}
private readonly Func<T, TProperty> selector;
public GenericComparer(Func<T, TProperty> selector)
{
this.selector = selector;
}
public bool Equals(T? x, T? y)
{
if (x == null || y == null) return false;
return Equals(selector(x), selector(y));
}
public int GetHashCode([DisallowNull] T obj)
{
object? value = selector(obj);
if (value == null) return obj.GetHashCode();
return value.GetHashCode();
}
}
This is all just pseudo code...
Ok here is my scenario, I have an incoming data stream that gets parsed into packets.
I have an IObservable<Packets> Packets
Each packet has a Packet ID, i.e. 1, 2, 3, 4
I want to create observables that only receive a specific ID.
so I do:
Packets.Where(p=>p.Id == 1)
for example... that gives me an IObservable<Packets> that only gives me packets of Id 1.
I may have several of these:
Packets.Where(p=>p.Id == 2)
Packets.Where(p=>p.Id == 3)
Packets.Where(p=>p.Id == 4)
Packets.Where(p=>p.Id == 5)
This essentially works, but the more Ids I want to select the more processing is required, i.e. the p=>p.Id will be run for every single Id, even after a destination Observable has been found.
How can I do the routing so that it is more efficient, something analogous:
Dictionary listeners;
listeners.GetValue(packet.Id).OnDataReceived(packet)
so that as soon as an id is picked up by one of my IObservables, then none of the others get to see it?
Updates
Added an extension based on Lee Campbell's groupby suggestion:
public static class IObservableExtensions
{
class RouteTable<TKey, TSource>
{
public static readonly ConditionalWeakTable<IObservable<TSource>, IObservable<IGroupedObservable<TKey, TSource>>> s_routes = new ConditionalWeakTable<IObservable<TSource>, IObservable<IGroupedObservable<TKey, TSource>>>();
}
public static IObservable<TSource> Route<TKey, TSource>(this IObservable<TSource> source, Func<TSource, TKey> selector, TKey id)
{
var grouped = RouteTable<TKey, TSource>.s_routes.GetValue(source, s => s.GroupBy(p => selector(p)).Replay().RefCount());
return grouped.Where(e => e.Key.Equals(id)).SelectMany(e => e);
}
}
It would be used like this:
Subject<Packet> packetSubject = new Subject<Packet>();
var packets = packetSubject.AsObservable();
packets.Route((p) => p.Id, 5).Subscribe((p) =>
{
Console.WriteLine("5");
});
packets.Route((p) => p.Id, 4).Subscribe((p) =>
{
Console.WriteLine("4");
});
packets.Route((p) => p.Id, 3).Subscribe((p) =>
{
Console.WriteLine("3");
});
packetSubject.OnNext(new Packet() { Id = 1 });
packetSubject.OnNext(new Packet() { Id = 2 });
packetSubject.OnNext(new Packet() { Id = 3 });
packetSubject.OnNext(new Packet() { Id = 4 });
packetSubject.OnNext(new Packet() { Id = 5 });
packetSubject.OnNext(new Packet() { Id = 4 });
packetSubject.OnNext(new Packet() { Id = 3 });
output is:
3, 4, 5, 4, 3
It only checks the Id for every group when it sees a new packet id.
Here's an operator that I wrote quite some time ago, but I think it does what you're after. I still think that a simple .Where is probably better - even with multiple subscribers.
Nevertheless, I wanted a .ToLookup for observables that operates like the same operator for enumerables.
It isn't memory efficient, but it implements IDisposable so that it can be cleaned up afterwards. It also isn't thread-safe so a little hardening might be required.
Here it is:
public static class ObservableEx
{
public static IObservableLookup<K, V> ToLookup<T, K, V>(this IObservable<T> source, Func<T, K> keySelector, Func<T, V> valueSelector, IScheduler scheduler)
{
return new ObservableLookup<T, K, V>(source, keySelector, valueSelector, scheduler);
}
internal class ObservableLookup<T, K, V> : IDisposable, IObservableLookup<K, V>
{
private IDisposable _subscription = null;
private readonly Dictionary<K, ReplaySubject<V>> _lookups = new Dictionary<K, ReplaySubject<V>>();
internal ObservableLookup(IObservable<T> source, Func<T, K> keySelector, Func<T, V> valueSelector, IScheduler scheduler)
{
_subscription = source.ObserveOn(scheduler).Subscribe(
t => this.GetReplaySubject(keySelector(t)).OnNext(valueSelector(t)),
ex => _lookups.Values.ForEach(rs => rs.OnError(ex)),
() => _lookups.Values.ForEach(rs => rs.OnCompleted()));
}
public void Dispose()
{
if (_subscription != null)
{
_subscription.Dispose();
_subscription = null;
_lookups.Values.ForEach(rs => rs.Dispose());
_lookups.Clear();
}
}
private ReplaySubject<V> GetReplaySubject(K key)
{
if (!_lookups.ContainsKey(key))
{
_lookups.Add(key, new ReplaySubject<V>());
}
return _lookups[key];
}
public IObservable<V> this[K key]
{
get
{
if (_subscription == null) throw new ObjectDisposedException("ObservableLookup");
return this.GetReplaySubject(key).AsObservable();
}
}
}
}
public interface IObservableLookup<K, V> : IDisposable
{
IObservable<V> this[K key] { get; }
}
You would use it like this:
IObservable<Packets> Packets = ...
IObservableLookup<int, Packets> lookup = Packets.ToLookup(p => p.Id, p => p, Scheduler.Default);
lookup[1].Subscribe(p => { });
lookup[2].Subscribe(p => { });
// etc
The nice thing with this is that you can subscribe to values by key before a value with that key has been produced by the source observable.
Don't forget to call lookup.Dispose() when done to clean up the resources.
I would suggest looking at GroupBy and then checking if there is a performance pay off. I assume there is, but is it significant?
Packets.GroupBy(p=>p.Id)
Example code with tests on how to use GroupBy as a type of router
var scheduler = new TestScheduler();
var source = scheduler.CreateColdObservable(
ReactiveTest.OnNext(100, 1),
ReactiveTest.OnNext(200, 2),
ReactiveTest.OnNext(300, 3),
ReactiveTest.OnNext(400, 4),
ReactiveTest.OnNext(500, 5),
ReactiveTest.OnNext(600, 6),
ReactiveTest.OnNext(700, 7),
ReactiveTest.OnNext(800, 8),
ReactiveTest.OnNext(900, 9),
ReactiveTest.OnNext(1000, 10),
ReactiveTest.OnNext(1100, 11)
);
var router = source.GroupBy(i=>i%4)
.Publish()
.RefCount();
var zerosObserver = scheduler.CreateObserver<int>();
router.Where(grp=>grp.Key == 0)
.Take(1)
.SelectMany(grp=>grp)
.Subscribe(zerosObserver);
var onesObserver = scheduler.CreateObserver<int>();
router.Where(grp => grp.Key == 1)
.Take(1)
.SelectMany(grp => grp)
.Subscribe(onesObserver);
var twosObserver = scheduler.CreateObserver<int>();
router.Where(grp => grp.Key == 2)
.Take(1)
.SelectMany(grp => grp)
.Subscribe(twosObserver);
var threesObserver = scheduler.CreateObserver<int>();
router.Where(grp => grp.Key == 3)
.Take(1)
.SelectMany(grp => grp)
.Subscribe(threesObserver);
scheduler.Start();
ReactiveAssert.AreElementsEqual(new[] { ReactiveTest.OnNext(400, 4), ReactiveTest.OnNext(800, 8)}, zerosObserver.Messages);
ReactiveAssert.AreElementsEqual(new[] { ReactiveTest.OnNext(100, 1), ReactiveTest.OnNext(500, 5), ReactiveTest.OnNext(900, 9)}, onesObserver.Messages);
ReactiveAssert.AreElementsEqual(new[] { ReactiveTest.OnNext(200, 2), ReactiveTest.OnNext(600, 6), ReactiveTest.OnNext(1000, 10) }, twosObserver.Messages);
ReactiveAssert.AreElementsEqual(new[] { ReactiveTest.OnNext(300, 3), ReactiveTest.OnNext(700, 7), ReactiveTest.OnNext(1100, 11)}, threesObserver.Messages);
You can use GroupBy to split the data. I would suggest you set up all subscriptions first and then activate your source. Doing so would result in one huge nested GroupBy query, but it is also possible to multi-cast your groups and subscribe to them individually. I wrote a small helper utility to do so below.
Because you still might want to add new routes after the source has been activated (done trough Connect), we use Replay to replay the groups. Replay is also a multi-cast operator so we wont need Publish to multi-cast.
public sealed class RouteData<TKey, TSource>
{
private IConnectableObservable<IGroupedObservable<TKey, TSource>> myRoutes;
public RouteData(IObservable<TSource> source, Func<TSource, TKey> keySelector)
{
this.myRoutes = source.GroupBy(keySelector).Replay();
}
public IDisposable Connect()
{
return this.myRoutes.Connect();
}
public IObservable<TSource> Get(TKey id)
{
return myRoutes.FirstAsync(e => e.Key.Equals(id)).Merge();
}
}
public static class myExtension
{
public static RouteData<TKey, TSource> RouteData<TKey, TSource>(this IObservable<TSource> source, Func<TSource, TKey> keySelector)
{
return new RouteData<TKey, TSource>(source, keySelector);
}
}
Example usage:
public class myPackage
{
public int Id;
public myPackage(int id)
{
this.Id = id;
}
}
class program
{
static void Main()
{
var source = new[] { 0, 1, 2, 3, 4, 5, 4, 3 }.ToObservable().Select(i => new myPackage(i));
var routes = source.RouteData(e => e.Id);
var subscription = new CompositeDisposable(
routes.Get(5).Subscribe(Console.WriteLine),
routes.Get(4).Subscribe(Console.WriteLine),
routes.Get(3).Subscribe(Console.WriteLine),
routes.Connect());
Console.ReadLine();
}
}
You may want to consider writing a custom IObserver that does your bidding. I've included an example below.
void Main()
{
var source = Observable.Range(1, 10);
var switcher = new Switch<int, int>(i => i % 3);
switcher[0] = Observer.Create<int>(val => Console.WriteLine($"{val} Divisible by three"));
source.Subscribe(switcher);
}
class Switch<TKey,TValue> : IObserver<TValue>
{
private readonly IDictionary<TKey, IObserver<TValue>> cases;
private readonly Func<TValue,TKey> idExtractor;
public IObserver<TValue> this[TKey decision]
{
get
{
return cases[decision];
}
set
{
cases[decision] = value;
}
}
public Switch(Func<TValue,TKey> idExtractor)
{
this.cases = new Dictionary<TKey, IObserver<TValue>>();
this.idExtractor = idExtractor;
}
public void OnNext(TValue next)
{
IObserver<TValue> nextCase;
if (cases.TryGetValue(idExtractor(next), out nextCase))
{
nextCase.OnNext(next);
}
}
public void OnError(Exception e)
{
foreach (var successor in cases.Values)
{
successor.OnError(e);
}
}
public void OnCompleted()
{
foreach (var successor in cases.Values)
{
successor.OnCompleted();
}
}
}
You would obviously need to implement idExtractor to extract the ids from your packet.
Given two lists of different types, is it possible to make those types convertible between or comparable to each other (eg with a TypeConverter or similar) so that a LINQ query can compare them? I've seen other similar questions on SO but nothing that points to making the types convertible between each other to solve the problem.
Collection Types:
public class Data
{
public int ID { get; set; }
}
public class ViewModel
{
private Data _data;
public ViewModel(Data data)
{
_data = data;
}
}
Desired usage:
public void DoMerge(ObservableCollection<ViewModel> destination, IEnumerable<Data> data)
{
// 1. Find items in data that don't already exist in destination
var newData = destination.Except(data);
// ...
}
It would seem logical that since I know how to compare an instance of ViewModel to an instance of Data I should be able to provide some comparison logic that LINQ would then use for queries like .Except(). Is this possible?
I assume that providing a projection from Data to ViewModel is problematic, so I'm offering another solution in addition to Jason's.
Except uses a hash set (if I recall correctly), so you can get similar performance by creating your own hashset. I'm also assuming that you are identifying Data objects as equal when their IDs are equal.
var oldIDs = new HashSet<int>(data.Select(d => d.ID));
var newData = destination.Where(vm => !oldIDs.Contains(vm.Data.ID));
You might have another use for a collection of "oldData" elsewhere in the method, in which case, you would want to do this instead. Either implement IEquatable<Data> on your data class, or create a custom IEqualityComparer<Data> for the hash set:
var oldData = new HashSet<Data>(data);
//or: var oldData = new HashSet<Data>(data, new DataEqualityComparer());
var newData = destination.Where(vm => !oldData.Contains(vm.Data));
I know this is late but there is a simpler syntax using Func that eliminates the need for a comparer.
public static class LinqExtensions
{
public static IEnumerable<TSource> Except<TSource, VSource>(this IEnumerable<TSource> first, IEnumerable<VSource> second, Func<TSource, VSource, bool> comparer)
{
return first.Where(x => second.Count(y => comparer(x, y)) == 0);
}
public static IEnumerable<TSource> Contains<TSource, VSource>(this IEnumerable<TSource> first, IEnumerable<VSource> second, Func<TSource, VSource, bool> comparer)
{
return first.Where(x => second.FirstOrDefault(y => comparer(x, y)) != null);
}
public static IEnumerable<TSource> Intersect<TSource, VSource>(this IEnumerable<TSource> first, IEnumerable<VSource> second, Func<TSource, VSource, bool> comparer)
{
return first.Where(x => second.Count(y => comparer(x, y)) == 1);
}
}
so with lists of class Foo and Bar
public class Bar
{
public int Id { get; set; }
public string OtherBar { get; set; }
}
public class Foo
{
public int Id { get; set; }
public string OtherFoo { get; set; }
}
one can run Linq statements like
var fooExceptBar = fooList.Except(barList, (f, b) => f.Id == b.Id);
var barExceptFoo = barList.Except(fooList, (b, f) => b.OtherBar == f.OtherFoo);
it's basically a slight variation on above but seems cleaner to me.
If you use this :
var newData = destination.Except(data.Select(x => f(x)));
You have to project 'data' to same type contained in 'destination', but using the code below you could get rid of this limitation :
//Here is how you can compare two different sets.
class A { public string Bar { get; set; } }
class B { public string Foo { get; set; } }
IEnumerable<A> setOfA = new A[] { /*...*/ };
IEnumerable<B> setOfB = new B[] { /*...*/ };
var subSetOfA1 = setOfA.Except(setOfB, a => a.Bar, b => b.Foo);
//alternatively you can do it with a custom EqualityComparer, if your not case sensitive for instance.
var subSetOfA2 = setOfA.Except(setOfB, a => a.Bar, b => b.Foo, StringComparer.OrdinalIgnoreCase);
//Here is the extension class definition allowing you to use the code above
public static class IEnumerableExtension
{
public static IEnumerable<TFirst> Except<TFirst, TSecond, TCompared>(
this IEnumerable<TFirst> first,
IEnumerable<TSecond> second,
Func<TFirst, TCompared> firstSelect,
Func<TSecond, TCompared> secondSelect)
{
return Except(first, second, firstSelect, secondSelect, EqualityComparer<TCompared>.Default);
}
public static IEnumerable<TFirst> Except<TFirst, TSecond, TCompared>(
this IEnumerable<TFirst> first,
IEnumerable<TSecond> second,
Func<TFirst, TCompared> firstSelect,
Func<TSecond, TCompared> secondSelect,
IEqualityComparer<TCompared> comparer)
{
if (first == null)
throw new ArgumentNullException("first");
if (second == null)
throw new ArgumentNullException("second");
return ExceptIterator<TFirst, TSecond, TCompared>(first, second, firstSelect, secondSelect, comparer);
}
private static IEnumerable<TFirst> ExceptIterator<TFirst, TSecond, TCompared>(
IEnumerable<TFirst> first,
IEnumerable<TSecond> second,
Func<TFirst, TCompared> firstSelect,
Func<TSecond, TCompared> secondSelect,
IEqualityComparer<TCompared> comparer)
{
HashSet<TCompared> set = new HashSet<TCompared>(second.Select(secondSelect), comparer);
foreach (TFirst tSource1 in first)
if (set.Add(firstSelect(tSource1)))
yield return tSource1;
}
}
Some may argue that's memory inefficient due to the use of an HashSet. But actually the Enumerable.Except method of the framework is doing the same with a similar internal class called 'Set' (I took a look by decompiling).
Your best bet is to provide a projection from Data to ViewModel so that you can say
var newData = destination.Except(data.Select(x => f(x)));
where f maps Data to ViewModel. You will need a IEqualityComparer<Data> too.
In an IObservable sequence (in Reactive Extensions for .NET), I'd like to get the value of the previous and current elements so that I can compare them. I found an example online similar to below which accomplishes the task:
sequence.Zip(sequence.Skip(1), (prev, cur) => new { Previous = prev, Current = cur })
It works fine except that it evaluates the sequence twice, which I would like to avoid. You can see that it is being evaluated twice with this code:
var debugSequence = sequence.Do(item => Debug.WriteLine("Retrieved an element from sequence"));
debugSequence.Zip(debugSequence.Skip(1), (prev, cur) => new { Previous = prev, Current = cur }).Subscribe();
The output shows twice as many of the debug lines as there are elements in the sequence.
I understand why this happens, but so far I haven't found an alternative that doesn't evaluate the sequence twice. How can I combine the previous and current with only one sequence evaluation?
There's a better solution to this I think, that uses Observable.Scan and avoids the double subscription:
public static IObservable<Tuple<TSource, TSource>>
PairWithPrevious<TSource>(this IObservable<TSource> source)
{
return source.Scan(
Tuple.Create(default(TSource), default(TSource)),
(acc, current) => Tuple.Create(acc.Item2, current));
}
I've written this up on my blog here: http://www.zerobugbuild.com/?p=213
Addendum
A further modification allows you to work with arbitrary types more cleanly by using a result selector:
public static IObservable<TResult> CombineWithPrevious<TSource,TResult>(
this IObservable<TSource> source,
Func<TSource, TSource, TResult> resultSelector)
{
return source.Scan(
Tuple.Create(default(TSource), default(TSource)),
(previous, current) => Tuple.Create(previous.Item2, current))
.Select(t => resultSelector(t.Item1, t.Item2));
}
#James World addendum looks great to me, if not for Tuple<>, which I almost always dislike: "Was .Item1 the previous? Or was it the current one? I can't remember. And what's the first argument to the selector, was it the previous item?".
For that part I liked #dcstraw definition of a dedicated ItemWithPrevious<T>. So there you go, putting the two together (hopefully I did not mix up previous with current) with some renaming and facilities:
public static class ObservableExtensions
{
public static IObservable<SortedPair<TSource>> CombineWithPrevious<TSource>(
this IObservable<TSource> source,
TSource initialValue = default(TSource))
{
var seed = SortedPair.Create(initialValue, initialValue);
return source.Scan(seed,
(acc, current) => SortedPair.Create(current, acc.Current));
}
public static IObservable<TResult> CombineWithPrevious<TSource, TResult>(
this IObservable<TSource> source,
Func<SortedPair<TSource>, TResult> resultSelector,
TSource initialValue = default(TSource))
{
var seed = SortedPair.Create(initialValue, initialValue);
return source
.Scan(seed,
(acc, current) => SortedPair.Create(current, acc.Current))
.Select(p => resultSelector(p));
}
}
public class SortedPair<T>
{
public SortedPair(T current, T previous)
{
Current = current;
Previous = previous;
}
public SortedPair(T current) : this(current, default(T)) { }
public SortedPair() : this(default(T), default(T)) { }
public T Current;
public T Previous;
}
public class SortedPair
{
public static SortedPair<T> Create<T>(T current, T previous)
{
return new SortedPair<T>(current, previous);
}
public static SortedPair<T> Create<T>(T current)
{
return new SortedPair<T>(current);
}
public static SortedPair<T> Create<T>()
{
return new SortedPair<T>();
}
}
Evaluating twice is an indicator of a Cold observable. You can turn it to a Hot one by using .Publish():
var pub = sequence.Publish();
pub.Zip(pub.Skip(1), (...
pub.Connect();
If you only need to access the previous element during subscription, this is probably the simplest thing that will work. (I'm sure there's a better way, maybe a buffer operator on IObservable? The documentation is pretty sparse at the moment, so I can't really tell you.)
EventArgs prev = null;
sequence.Subscribe(curr =>
{
if (prev != null)
{
// Previous and current element available here
}
prev = curr;
});
EventArgs is just a stand-in for the type of your event's argument.
It turns out you can use a variable to hold the previous value and refer to it and reassign it within the chain of IObservable extensions. This even works within a helper method. With the code below I can now call CombineWithPrevious() on my IObservable to get a reference to the previous value, without re-evaluating the sequence.
public class ItemWithPrevious<T>
{
public T Previous;
public T Current;
}
public static class MyExtensions
{
public static IObservable<ItemWithPrevious<T>> CombineWithPrevious<T>(this IObservable<T> source)
{
var previous = default(T);
return source
.Select(t => new ItemWithPrevious<T> { Previous = previous, Current = t })
.Do(items => previous = items.Current);
}
}
I'm looking for a creative pattern to enumerate two IEnumerable<>'s synchronized.
If I was making something up and adding to the C# syntax I might write:
foreach(var firstItem, var secondItem in this.ListOne, this.ListTwo)
{
if (firstItem.Prop == secondItem.Prop)
WorkSomeMagic(secondItem);
DoSomethingElse(firstItem);
}
Now, obviously that doesn't exist. What patterns have people used to accomplish something similar when dealing with enumerations that aren't accessible by index? Keep in mind, what is inside my pseudo-foreach would be more complex; I simplified for the example.
You're looking for Zip, which is new in .NET 4 or you can use the implementation here:
Is there a zip-like method in .Net?
I usually do the following:
using (IEnumerator<int> e1 = this.ListOne.GetEnumerator(),
e2 = this.ListTwo.GetEnumerator()) {
while (e1.MoveNext() && e2.MoveNext()) {
...
}
}
Or write an extension method:
public static void EnumerateWith<T>(this IEnumerable<T> left,
IEnumerable<T> right, Action<T,T> del) {
using (IEnumerator<T> l = left.GetEnumerator(),
r = right.GetEnumerator()) {
while (l.MoveNext() && r.MoveNext()) {
del(l.Current,r.Current);
}
}
}
ListOne.EnumerateWith(ListTwo, (left, right) => {
...
});
As with any general C# question, this will probably have 10 good answers posted before VS2008 even loads. Instead of that rat race, I'll come up with an offbeat "anti-pattern" you should never use. In fact, anyone writing mission critical code, please stop reading now.
using System;
using System.Collections.Generic;
using System.Linq;
class EnumTwoLists
{
static void Main(string[] args)
{
var left = new List<int>();
var right = new List<DateTime>();
var demo = new LinqAbuse<int, DateTime>(left, right);
demo.Populate(40, s => s * s, d => new DateTime(2009, d / 31 + 1, d % 31 + 1));
demo.Enumerate( (s, d) => Console.WriteLine(String.Format("Executing arbitrary code with {0} and {1}", s, d)) );
}
}
class LinqAbuse<T1, T2>
{
public LinqAbuse(List<T1> l, List<T2> r)
{
left = l;
right = r;
}
List<T1> left;
List<T2> right;
public void Populate(int size, Func<int, T1> leftGenerator, Func<int, T2> rightGenerator)
{
new int[size].Aggregate(0, (index, empty) => PopulateWrapper(left, right, leftGenerator, rightGenerator, index));
}
int PopulateWrapper(List<T1> left, List<T2> right, Func<int, T1> leftGenerator, Func<int, T2> rightGenerator, int index)
{
left.Add(leftGenerator(index));
right.Add(rightGenerator(index));
return ++index;
}
public void Enumerate(Action<T1, T2> loopBody)
{
left.Join(right, l => "", r => "",
(l, r) => ActionWrapper(l, r, loopBody),
new CartesianComparer<object>(right.Count))
.ToList();
}
object ActionWrapper(T1 x, T2 y, Action<T1, T2> action)
{
action(x, y);
return null;
}
}
class CartesianComparer<T> : IEqualityComparer<T>
{
public CartesianComparer(int _size)
{
size = _size;
equalsCounter = (size * (size - 1) >> 1) + size; // Combinations(size, 2) + (size - trueCounter)
}
private int size;
private int equalsCounter;
private int trueCounter = 0;
public bool Equals(T x, T y)
{
if (0 < --equalsCounter)
return false;
equalsCounter = size - ++trueCounter;
return true;
}
public int GetHashCode(T obj)
{
return 0;
}
}
Aww, isn't she cute? (alternate caption: Mommy, why is Anders crying?)
Ignoring checks for nulls and whatnot:
IEnumerable<T1> first;
IEnumerable<T2> second;
using (IEnumerator<T1> e1 = first.GetEnumerator()) {
using (IEnumerator<T2> e2 = second.GetEnumerator()) {
while (e1.MoveNext() && e2.MoveNext()) {
// do something eith e1.Current and e2.Current
}
}
}
I know this question is old but for anyone coming to this question now you can build on Jason's answer and JaredPar's answer with C#7's ValueTuple, which will give you a syntax similar to the original question. You may need to install the nuget package System.ValueTuple.
If you declare an extension method something like this:
internal static class EnumerableExtensions
{
internal static IEnumerable<(T1, T2)> EnumerateWith<T1, T2>(this IEnumerable<T1> first, IEnumerable<T2> second)
{
using (var firstEnumerator = first.GetEnumerator())
using (var secondEnumerator = second.GetEnumerator())
{
while(firstEnumerator.MoveNext() && secondEnumerator.MoveNext())
{
yield return (firstEnumerator.Current, secondEnumerator.Current);
}
}
}
}
Then you can use it like this:
List<Foo> foos = new List<Foo>()
{
new Foo(),
new Foo(),
new Foo()
};
List<Bar> bars = new List<Bar>()
{
new Bar(),
new Bar(),
new Bar()
};
foreach((Foo foo, Bar bar) in foos.EnumerateWith(bars))
{
Console.WriteLine(foo.ID);
Console.WriteLine(bar.ID);
}