How to observe Immutable List NotifyCollectionChanged? - c#

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.

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);
}
});

After some research, I have a answser for my own question.
The best solution should be Levenshtein distance.
The computational process roughly as follows:
Determine insert delete substitution costs. (insert=1, delete=1, substitution=2)
Calculate levenshtein distance and get matrix.
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.

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++;
}
});
}

Related

Is there a Linq equivalent to the unix command uniq

Every search I make assumes "Distinct()", but this is NOT my requirement. I just wish to remove all the repeats. Are there any options using linq (i.e. the Enumerable extensions) ?
For example (in C#)
int[] input = new [] {1,2,3,3,4,5,5,5,6,6,5,4,4,3,2,1,6};
int[] expected = new [] {1,2,3,4,5,6,5,4,3,2,1,6};
You are asking for non-repeating elements, not unique elements. LINQ-to-Objects operations are essentially iterators. You could write your own iterator method that only yields the first time an item is encountered, eg:
public static IEnumerable<int> DistinctUntilChanged(this IEnumerable<int> source)
{
int? previous=null;
foreach(var item in source)
{
if (item!=previous)
{
previous=item;
yield return item;
}
}
}
var input = new [] {1,2,3,3,4,5,5,5,6,6,5,4,4,3,2,1,6};
var result=input.DistinctUntilChanged().ToArray();
The result will be :
{1,2,3,4,5,6,5,4,3,2,1,6};
UPDATE
Another option is to use Observable.DistinctUntilChanged from the System.Reactive Library, eg:
var input = new[] { 1, 2, 3, 3, 4, 5, 5, 5, 6, 6, 5, 4, 4, 3, 2, 1, 6 };
var result = input.ToObservable()
.DistinctUntilChanged()
.ToEnumerable()
.ToArray();
System.Reactive, and Reactive Extensions are meant to handle sequences of events using the basic LINQ operators and more. It's easy to convert between Observable and Enumerable though, with ToObservable() and ToEnumerable(), so they can be used to handle any collection. After all, an event sequence is similar to an "infinite" sequence
UPDATE 2
In case there's any confusion about the use of int? to store the previous number, it's to allow easy comparison even with the first element of the source without actually calling First() on it. If it was ,eg int previous=0; and the first element was 0, the comparison would filter out the first element.
By using an int? in C# or an int option in F# or a Maybe<int> if we have a Maybe monad we can differentiate between no initial value and an initial value of 0.
Observable.DistinctUntilChanged uses a flag to check whether we are checking the first element. The equivalent code would be:
public static IEnumerable<int> NonRepeating(this IEnumerable<int> source)
{
int previous =0;
bool isAssigned=false;
foreach (var item in source)
{
if (!isAssigned || item != previous)
{
isAssigned = true;
previous = item;
yield return item;
}
}
}
MoreLINQ
Finally, one can use the GroupAdjacent method from the MoreLinq library to group repeating items together. Each group contains the repeating source elements. In this particular case though we only need the key values:
var result = input.GroupAdjacent(i => i).Select(i => i.Key).ToArray();
The nice thing about GroupAdjacent is that the elements can be transformed while grouping, eg :
input.GroupAdjacent(i => i,i=>$"Number {i}")
would return groupings of strings.
It is possible with linq, although for performance and readability a simple for loop would probably be the better option.
int[] input = new[] { 1, 2, 3, 3, 4, 5, 5, 5, 6, 6, 5, 4, 4, 3, 2, 1, 6 };
var result = input.Where((x, i) => i == 0 || x != input[i - 1]).ToArray();

IEnumerable items disappearing before being used using LINQ Where

I'm fairly new to programming and I've been playing around with writing some random functions.
I wrote the below function which works loosely based on eratosthenes sieve. Initially though, I was having a problem with the updatedEntries IEnumerable.
The updatedEntites was sort of populating (something to do with deferred execution I gather - in debug mode the 'current' was null but the results view contained the relevant items) but when the RemoveWhere was applied to oddPrimesAndMultiples the items in updatedEntries disappeared even though I don't see why they should still be linked to the items in oddPrimesAndMultiples. (I could just be completely misunderstanding what's going on of course and the problem might be something else entirely!)
The problem doesn't arise if I change updatedEntries to a List rather than an IEnumerable and I've actually now rewritten that statement without using LINQ to (potentially?) make better use of the fact I'm using a SortedSet anyway...but I would still like to know why the issue arose in the first place!
Here is my code:
public static IEnumerable<int> QuickPrimes()
{
int firstPrime = 2;
int firstOddPrime = 3;
int currentValue = firstOddPrime;
int currentMinimumMultiple;
SortedSet<Tuple<int, int>> oddPrimesAndMultiples = new SortedSet<Tuple<int, int>>() { new Tuple<int, int> (firstOddPrime, firstOddPrime) };
IEnumerable<Tuple<int, int>> updatedEntries;
yield return firstPrime;
yield return firstOddPrime;
while (true)
{
currentMinimumMultiple = oddPrimesAndMultiples.First().Item1;
while (currentValue < currentMinimumMultiple)
{
yield return currentValue;
oddPrimesAndMultiples.Add(new Tuple<int, int> (currentValue * 3, currentValue));
currentValue += 2;
}
updatedEntries = oddPrimesAndMultiples.Where(tuple => tuple.Item1 == currentMinimumMultiple)
.Select(t => new Tuple<int, int>(t.Item1 + 2 * t.Item2, t.Item2));
oddPrimesAndMultiples.RemoveWhere(t => t.Item1 == currentMinimumMultiple);
oddPrimesAndMultiples.UnionWith(updatedEntries);
currentValue += 2;
}
}
and the main where I'm testing the function:
static void Main(string[] args)
{
foreach(int prime in Problems.QuickPrimes())
{
Console.WriteLine(prime);
if (prime > 20) return;
}
}
Many thanks in advance!
The trap is that updatedEntries is defined in one line, but actually executed later.
To bring it back to the basics, see this code snippet (from Linqpad):
var ints = new SortedSet<int>( new[] { 1,2,3,4,5,6,7,8,9,10});
var updatedEntries = ints.Where(i => i > 5); // No ToList()!
updatedEntries.Dump();
This shows 6, 7, 8, 9, 10.
ints.RemoveWhere(i => i > 7);
updatedEntries.Dump();
Now this shows 6, 7, because updatedEntries is re-executed.
ints.UnionWith(updatedEntries);
This adds 6, 7, while you expected it to add the first listing 6, 7, 8, 9, 10.
So when defining an IEnumerable you should always be aware of when it's actually executed. It always acts on the state of the program at that particular point.

Finding mode in List of integers [duplicate]

This question already has answers here:
How to find the Mode in Array C#? [duplicate]
(4 answers)
Closed 7 years ago.
How can I find the mode of a list of numbers? I know the logic of it (I think) but I don't know how to implement that logic or convert what my brain thinks into workable code.
This is what I know:
I need to have a loop that goes through the list one time to see how many times a number is repeated and an array to save the times a number is repeated. I also need to tell my program to discard the lesser amount once a larger one is found.
A linq approach, more concise but almost certainly less efficient than Yeldar Kurmangaliyev's:
int FindMode(IEnumerable<int> data)
{
return data
.GroupBy(n => n)
.Select(x => new { x.Key, Count = x.Count() })
.OrderByDescending(a => a.Count)
.First()
.Key;
}
This does not handle the case where data is empty, nor where there are two or more data points with the same frequency in the data set.
Yes, you are right:
Let we have a list of numbers:
List<int> myValues = new List<int>(new int[] { 1, 3, 3, 3, 7, 7 } );
You need to have a loop that goes through the list one time:
foreach (var val in myValues)
{
}
to see how many times a number is repeated in array to save the times a number is repeated:
Dictionary<int, int> repetitions = new Dictionary<int, int>();
foreach (var val in myValues)
{
if (repetitions.ContainsKey(val))
repetitions[val]++; // Met it one more time
else
repetitions.Add(val, 1); // Met it once, because it is not in dict.
}
Now, your dictionary repetitions stores how many (exactly value) times key value repeated.
Then, you need to find the record of mode (i.e. record with the highest time of repetitions (i.e. highest value)) and take this one. LINQ will help us - let's sort the array by value and take the last one...or sort it descending and take the first one. Actually, that's the same in terms of result and productivity.
var modeRecord = repetitions.OrderByDescending(x => x.Value).First();
// or
var modeRecord = repetitions.OrderBy(x => x.Value).Last();
Here it is! Here we have a mode:
List<int> myValues = new List<int>(new int[] { 1, 3, 3, 3, 7, 7 } );
Dictionary<int, int> repetitions = new Dictionary<int, int>();
foreach (var val in myValues)
{
if (repetitions.ContainsKey(val))
repetitions[val]++; // Met it one more time
else
repetitions.Add(val, 1); // Met it once, because it is not in dict.
}
var modeRecord = repetitions.OrderByDescending(x => x.Value).First();
Console.WriteLine("Mode is {0}. It meets {1} times in an list", modeRecord.Key, modeRecord.Value);
Your mode calculation logic is good. All you need is following your own instructions in a code :)
Here's an alternative LINQ approach:
var values = new int[] { 1, 3, 3, 3, 7, 7 };
var mode =
values
.Aggregate(
new { best = 0, best_length = 0, current = 0, current_length = 0 },
(a, n) =>
{
var current_length = 1 + (a.current == n ? a.current_length : 0);
var is_longer = current_length > a.best_length;
return new
{
best = is_longer ? n : a.best,
best_length = is_longer ? current_length : a.best_length,
current = n,
current_length,
};
}).best;

Get all possible distinct triples using LINQ

I have a List contains these values: {1, 2, 3, 4, 5, 6, 7}. And I want to be able to retrieve unique combination of three. The result should be like this:
{1,2,3}
{1,2,4}
{1,2,5}
{1,2,6}
{1,2,7}
{2,3,4}
{2,3,5}
{2,3,6}
{2,3,7}
{3,4,5}
{3,4,6}
{3,4,7}
{3,4,1}
{4,5,6}
{4,5,7}
{4,5,1}
{4,5,2}
{5,6,7}
{5,6,1}
{5,6,2}
{5,6,3}
I already have 2 for loops that able to do this:
for (int first = 0; first < test.Count - 2; first++)
{
int second = first + 1;
for (int offset = 1; offset < test.Count; offset++)
{
int third = (second + offset)%test.Count;
if(Math.Abs(first - third) < 2)
continue;
List<int> temp = new List<int>();
temp .Add(test[first]);
temp .Add(test[second]);
temp .Add(test[third]);
result.Add(temp );
}
}
But since I'm learning LINQ, I wonder if there is a smarter way to do this?
UPDATE: I used this question as the subject of a series of articles starting here; I'll go through two slightly different algorithms in that series. Thanks for the great question!
The two solutions posted so far are correct but inefficient for the cases where the numbers get large. The solutions posted so far use the algorithm: first enumerate all the possibilities:
{1, 1, 1 }
{1, 1, 2 },
{1, 1, 3 },
...
{7, 7, 7}
And while doing so, filter out any where the second is not larger than the first, and the third is not larger than the second. This performs 7 x 7 x 7 filtering operations, which is not that many, but if you were trying to get, say, permutations of ten elements from thirty, that's 30 x 30 x 30 x 30 x 30 x 30 x 30 x 30 x 30 x 30, which is rather a lot. You can do better than that.
I would solve this problem as follows. First, produce a data structure which is an efficient immutable set. Let me be very clear what an immutable set is, because you are likely not familiar with them. You normally think of a set as something you add items and remove items from. An immutable set has an Add operation but it does not change the set; it gives you back a new set which has the added item. The same for removal.
Here is an implementation of an immutable set where the elements are integers from 0 to 31:
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System;
// A super-cheap immutable set of integers from 0 to 31 ;
// just a convenient wrapper around bit operations on an int.
internal struct BitSet : IEnumerable<int>
{
public static BitSet Empty { get { return default(BitSet); } }
private readonly int bits;
private BitSet(int bits) { this.bits = bits; }
public bool Contains(int item)
{
Debug.Assert(0 <= item && item <= 31);
return (bits & (1 << item)) != 0;
}
public BitSet Add(int item)
{
Debug.Assert(0 <= item && item <= 31);
return new BitSet(this.bits | (1 << item));
}
public BitSet Remove(int item)
{
Debug.Assert(0 <= item && item <= 31);
return new BitSet(this.bits & ~(1 << item));
}
IEnumerator IEnumerable.GetEnumerator() { return this.GetEnumerator(); }
public IEnumerator<int> GetEnumerator()
{
for(int item = 0; item < 32; ++item)
if (this.Contains(item))
yield return item;
}
public override string ToString()
{
return string.Join(",", this);
}
}
Read this code carefully to understand how it works. Again, always remember that adding an element to this set does not change the set. It produces a new set that has the added item.
OK, now that we've got that, let's consider a more efficient algorithm for producing your permutations.
We will solve the problem recursively. A recursive solution always has the same structure:
Can we solve a trivial problem? If so, solve it.
If not, break the problem down into a number of smaller problems and solve each one.
Let's start with the trivial problems.
Suppose you have a set and you wish to choose zero items from it. The answer is clear: there is only one possible permutation with zero elements, and that is the empty set.
Suppose you have a set with n elements in it and you want to choose more than n elements. Clearly there is no solution, not even the empty set.
We have now taken care of the cases where the set is empty or the number of elements chosen is more than the number of elements total, so we must be choosing at least one thing from a set that has at least one thing.
Of the possible permutations, some of them have the first element in them and some of them do not. Find all the ones that have the first element in them and yield them. We do this by recursing to choose one fewer elements on the set that is missing the first element.
The ones that do not have the first element in them we find by enumerating the permutations of the set without the first element.
static class Extensions
{
public static IEnumerable<BitSet> Choose(this BitSet b, int choose)
{
if (choose < 0) throw new InvalidOperationException();
if (choose == 0)
{
// Choosing zero elements from any set gives the empty set.
yield return BitSet.Empty;
}
else if (b.Count() >= choose)
{
// We are choosing at least one element from a set that has
// a first element. Get the first element, and the set
// lacking the first element.
int first = b.First();
BitSet rest = b.Remove(first);
// These are the permutations that contain the first element:
foreach(BitSet r in rest.Choose(choose-1))
yield return r.Add(first);
// These are the permutations that do not contain the first element:
foreach(BitSet r in rest.Choose(choose))
yield return r;
}
}
}
Now we can ask the question that you need the answer to:
class Program
{
static void Main()
{
BitSet b = BitSet.Empty.Add(1).Add(2).Add(3).Add(4).Add(5).Add(6).Add(7);
foreach(BitSet result in b.Choose(3))
Console.WriteLine(result);
}
}
And we're done. We have generated only as many sequences as we actually need. (Though we have done a lot of set operations to get there, but set operations are cheap.) The point here is that understanding how this algorithm works is extremely instructive. Recursive programming on immutable structures is a powerful tool that many professional programmers do not have in their toolbox.
You can do it like this:
var data = Enumerable.Range(1, 7);
var r = from a in data
from b in data
from c in data
where a < b && b < c
select new {a, b, c};
foreach (var x in r) {
Console.WriteLine("{0} {1} {2}", x.a, x.b, x.c);
}
Demo.
Edit: Thanks Eric Lippert for simplifying the answer!
var ints = new int[] { 1, 2, 3, 4, 5, 6, 7 };
var permutations = ints.SelectMany(a => ints.Where(b => (b > a)).
SelectMany(b => ints.Where(c => (c > b)).
Select(c => new { a = a, b = b, c = c })));

IEnumerable Best Practice of removing int item

I have a IEmunerable list with N items for example: 23, 1, 38.....
The needed logic is, when looping thru the list:
1: find if 1exist
2: if 1 exist, find 2
3: if 2 exist, find 3
4: If 3 exist, remove 3 from the current list.
My current approach is:
foreach(var x in someIntList)
{
if(x==1)
{
if(someIntList.Any(y => y==2))
{
if(someIntList.Any(z => z==3))
{
//This is the shortest code i can think of, but apparently its wrong. Error saying there is no Except method for IEmunerable<Int> ?
someIntList = someIntList.Except(3);
}
}
}
}
You need to pass IEnumerable<int> to except, like this
someIntList = someIntList.Except(new[] {3});
read more about Except on MSDN
To remove better use this, I can't imagine shorter version:
if(someIntList.Contains(1)&&someIntList.Contains(2)&&someIntList.Contains(3))
{
someIntList.Remove(3); // **if it's a list**
someIntList = someIntList.Except(3); //**if it's a IEnumerable**
}
Snippet
var x = new List<int> {5, 4, 3, 2, 1};
if(x.Contains(1) && x.Contains(2) && x.Contains(3)) x.Remove(3);
Just in case...
IEnumerable<int> y = new List<int> {5,4,3,2,1};
So if you are getting an IEnumerable from your method.
var x = y.ToList()
If you need to remove all, x.RemoveAll(z=>z == 3);
It's not clear why you're looping to start with, or using Any instead of Contains:
if (someIntList.Contains(1) && someIntList.Contains(2) && someIntList.Contains(3))
{
someIntList = someIntList.Where(x => x != 3); // Possibly with ToList()?
}
You probably don't want to use Except as that's a set-based operation - if your original list contains duplicates, they will be removed if you use Except.
Note that this will remove all occurrences of 3 - is that what you want, or do you just want to remove the first occurrence of 3?

Categories

Resources