0

As we know, we can observe collection changed using ObservableCollection.

That's s fine.

But how to handle ImmutableList changed?

For example:I have IObservable<ImmutableArray<int>> and sequence of this steam maybe:

First: 1, 2, 3, 4, 5

Second: 1, 2, 3, 4, 5, 6 <----(maybe some performance issue when binding to view.)

Third: 3, 4

Is there any elegant way (or some library) can convert IObservable<ImmutableArray<int>> to ObservableCollection<int> ?

And then we can observe ObservableCollection notification event:

First: add event 1, 2, 3, 4, 5

Second: add event 6, 7 <---- (That's cool!)

Third: remove event 1, 2, 5, 6

Very thanks.

chansey
  • 1,266
  • 9
  • 20
  • You can't (or really *should not*) use `ObservableCollection` with an `ImmutableArray` because `ObservableCollection` implements the mutable `Collection` interface. – Matthew Watson Jan 01 '16 at 10:01
  • 1
    I'm struggling to understand the intent behind this question. `ImmutableList` is immutable, ie it doesn't change. So why would you want to listen for collection changed events on such a list? – David Arno Jan 01 '16 at 10:20
  • I understand ObservableCollection can not be used with ImmutableArray. So I am finding a elegant way to convert IObservable> to ObservableCollection . – chansey Jan 01 '16 at 10:22
  • @DavidArno ImmutableList is immutable and it doesn't change. But IObservable> can represented "collection changed". the intent behind this question is : my model is a event souce. – chansey Jan 01 '16 at 10:37
  • It's not the array that's changing, it's the property that holds a reference to the array that's changing. In this respect, perhaps you should be using [`Subject>`](https://msdn.microsoft.com/en-us/library/hh229173%28v=vs.103%29.aspx) – Matthew Watson Jan 01 '16 at 10:38
  • @MatthewWatson Yes, is there any elegant way to handle this situation? (In my question, I use IObservable, but they are the same meaning.) – chansey Jan 01 '16 at 10:43

3 Answers3

2

This might be a bit of a naive approach, but is this the kind of thing you had in mind?

source
    .Subscribe(ia =>
    {
        var ia2 = ia.ToArray();
        var adds = ia2.Except(oc).ToArray();
        var removes = oc.Except(ia2).ToArray();
        foreach (var a in adds)
        {
            oc.Add(a);
        }
        foreach (var r in remove)
        {
            oc.Remove(r);
        }
    });
Enigmativity
  • 113,464
  • 11
  • 89
  • 172
  • Yes, this is what I want, thanks! But this solution don't consider item index (For instance:old array is 1 2 3 and insert 4 to a[1], new array is 1 4 2 3). Since I think this problem should be very common, so I am looking for some engineering solution. – chansey Jan 01 '16 at 13:14
  • I said it as a naive approach. But, you may just have to take a variation on this. Perhaps simply running though the indices and replace values that aren't the same and remove or add any surplus? – Enigmativity Jan 01 '16 at 13:24
0

After some research, I have a answser for my own question.

The best solution should be Levenshtein distance.

The computational process roughly as follows:

  1. Determine insert delete substitution costs. (insert=1, delete=1, substitution=2)

  2. Calculate levenshtein distance and get matrix.

  3. Backtrace matrix for shortest path and alignment. (it's very like A* pathfinding, setting backtrace point when generate matrix and get shorest path following backtrace)

Therefore this question could be closed.

chansey
  • 1,266
  • 9
  • 20
0

I actually wrote a nuget package that does this automatically for you

https://github.com/Weingartner/ReactiveCompositeCollections

Part of the code uses diffs between immutable lists to generate ObservableCollection change events.

The code that does the diffing uses DiffLib

    public static IObservable<List<DiffElement<T>>> 
        ChangesObservable<T>
         ( this ICompositeList<T> source
         , IEqualityComparer<T>comparer = null 
         )
    {
        return source
            .Items // IObservable<ImmutableList<T>>
            .StartWith(ImmutableList<T>.Empty)
            .Buffer(2, 1).Where(b => b.Count == 2)
            .Select(b =>
                    {
                        var sections = Diff.CalculateSections(b[0], b[1], comparer);
                        var alignment = Diff.AlignElements
                            (b[0], b[1], sections, new BasicReplaceInsertDeleteDiffElementAligner<T>());
                        return alignment.ToList();
                    });
    }

which in another method can be converted into an ObservableCollection

    internal ReadOnlyObservableCollection
        ( ICompositeList<T> list
        , System.Collections.ObjectModel.ObservableCollection<T> collection
        , IEqualityComparer<T> eq
        ) : base(collection)
    {
        _List = list;
        _Collection = collection;

        _Disposable = list.ChangesObservable(eq)
            .Subscribe(change =>
            {
                int i = 0;
                foreach (var diff in change)
                {
                    switch (diff.Operation)
                    {
                        case DiffOperation.Match:
                            break;
                        case DiffOperation.Insert:
                            _Collection.Insert(i, diff.ElementFromCollection2.Value);
                            break;
                        case DiffOperation.Delete:
                            _Collection.RemoveAt(i);
                            i--;
                            break;
                        case DiffOperation.Replace:
                            _Collection[i] = diff.ElementFromCollection2.Value;
                            break;
                        case DiffOperation.Modify:
                            _Collection[i] = diff.ElementFromCollection2.Value;
                            break;
                        default:
                            throw new ArgumentOutOfRangeException();
                    }
                    i++;
                }
            });
    }
bradgonesurfing
  • 30,949
  • 17
  • 114
  • 217