Rx Workshop: Reactive Coincidence

Sign in to queue

Description

Learn how to model events with duration and how to use the LINQ Join operator to express complex queries involving coincidence.

Download the Challenge

Embed

Download

Download this episode

The Discussion

  • User profile image
    aL_

    awsome stuff.. Smiley

     

  • User profile image
    BennyG

    Am I allowed to post the solution of the challange?

    Benny

  • User profile image
    AceHack

    Here is what I came up with

                var query = Observable.Join(
                    mouseDown, 
                    mouseMove, 
                    _ => mouseUp, 
                    _ => Observable.Empty<Unit>(), 
                    (_, r) => r)
                    .Publish(evt => evt.Zip(evt.Skip(1), 
                        (previous, current) => Subtract(current, previous)));
    

    I am having difficulty translating this into query comprehension syntax.  Anyone figure that out?  Thanks.

  • User profile image
    AceHack

    Alright here is a working query comprehension version

    var query = from start in mouseDown
                join move in mouseMove
                on mouseUp equals Observable.Empty<Unit>() into moveGroup
                let drag = moveGroup.Zip(moveGroup.Skip(1),
                    (prev, curr) => Subtract(curr, prev))
                from delta in drag
                select delta;
    

    Still curious if this could be cleaner.  Thanks.

     

  • User profile image
    MrLovebucket

    Here is my solution:

    var query = Observable.Join(
    mouseDown,
    mouseMove.Buffer(2),
    _ => mouseUp,
    m => Observable.Empty<Unit>(),
    (_, r) => Subtract(r.Last(),r.First()));

  • User profile image
    BennyG

    I think my solution is close to the optimum!?

    But I had to modify the subscibtion from deltas to absolute points.

       var query = mouseDown.Join(mouseMove, l => mouseUp, r => Observable.Empty<Point>(),(l,r)=> Subtract(r,l));
    
               query.Subscribe(pos=>
                {
                    Canvas.SetLeft(image, pos.X);
                    Canvas.SetTop(image, pos.Y);
                });

  • User profile image
    AceHack

    I notied there is a bug when releasing the mouse button from outside of the window.  Here is a fix that will merge mouseUp and mouseLeave to accomplish the task.  What is normal UI guidance for drag and drop when going outside the window?  Thanks.

                var mouseLeave = from evt in Observable.FromEventPattern<MouseEventHandler, MouseEventArgs>(h => MouseLeave += h, h => MouseLeave -= h)
                                 select evt.EventArgs.GetPosition(this);
                var query = from start in mouseDown
                            join move in mouseMove.Buffer(2)
                            on mouseUp.Merge(mouseLeave) equals Observable.Empty<Unit>()
                            select Subtract(move.Last(), move.First());
    

  • User profile image
    AceHack

    Hey what happened to the challenges.  They are showing up 404.  I was trying to dowload again.  Thanks.

  • User profile image
    Ivar_​Rummelhoff

    Here is my (edited) suggestion:

    mouseMove
    .Buffer(2, 1)
    .Select(b => b[1] - b[0])
    .Window(mouseDown, _ => mouseUp)
    .Concat();
    

    The Subtract method is not needed.

  • User profile image
    Malc
    What I ended up with in query and fluent:
                var query = from down in mouseDown
                            join delta in
                                from move in mouseMove.Buffer(2)
                                select Subtract(move.Last(), move.First())
                                on mouseUp equals Observable.Empty<Unit>() 
                            select delta;
    //            var query = Observable.Join(
    //                mouseDown,
    //                mouseMove.Buffer(2).Select(b => Subtract(b.Last(), b.First())),
    //                _ => mouseUp,
    //                _ => Observable.Empty<EventArgs>(),
    //                (_, r) => r);
    ...though interested if anyone came up with a way to buffer then collapse after the join (I battered my head against that approach for a while before going for this).
    Also ended up with the same sln as BennyG first time after missreading the subscribe lambda...

  • User profile image
    JJJ

    Here is a solution that uses the Scan method:

    var query = mouseDown.Join(
    mouseMove
    .Scan(new { Prev = new Point(), Delta = new Point() },
    (current, next) => new { Prev = next, Delta = Subtract(next, current.Prev) })
    .Select(x => x.Delta),
    down => mouseUp,
    delta => Observable.Empty<Unit>(),
    (down, delta) => delta);

  • User profile image
    Geoffreyk

    reopening comments

  • User profile image
    Anaristos

    My solution was based on a similar handling by SelectMany().

    private void DragAndDrop()
            {
                var mousedown = Observable.FromEventPattern<MouseButtonEventHandler, MouseButtonEventArgs>(h => image.MouseDown += h,
                                                                                                           h => image.MouseDown -= h)
                                          .Select(e => e.EventArgs.GetPosition(image));
    
                var mousemove = Observable.FromEventPattern<MouseEventHandler, MouseEventArgs>(h => image.MouseMove += h,
                                                                                               h => image.MouseMove -= h)
                                          .Select(e => e.EventArgs.GetPosition(canvas));
    
                var mouseup = Observable.FromEventPattern<MouseButtonEventHandler, MouseButtonEventArgs>(h => image.MouseUp += h,
                                                                                                         h => image.MouseUp -= h)
                                        .Select(e => e.EventArgs.GetPosition(image));
    
                var query = Observable.Join( mousedown
                                            ,mousemove
                                            ,p1 => mouseup
                                            ,p2 => Observable.Empty<Point>()
                                            ,(p1, p2) => new { X = p2.X - p1.X, Y = p2.Y - p1.Y }
                                          )
                                     .Subscribe(p => { Canvas.SetLeft(image, p.X); Canvas.SetTop(image, p.Y); });
            }
     
    

Add Your 2 Cents