The result of the first observable (itemsCache.GetAllKeys()) is a list of strings. If that list contains certain string (DOKUMENTI_KEY), then the second observable should be called. If the first observable doesn't contain the string, then an empty list should be returned.
public static IObservable<TResult> If<TSource, TResult>(
this IObservable<TSource> source,
Func<TSource, bool> predicate,
Func<TSource, IObservable<TResult>> thenSource,
Func<TSource, IObservable<TResult>> elseSource)
{
return source
.SelectMany(
value => predicate(value)
? thenSource(value)
: elseSource(value));
}
public IObservable<List<Dokument>> GetDokumenti()
{
return Observable.If(
() => itemsCache.GetAllKeys().SelectMany(x => x).Contains(DOKUMENTI_KEY).GetAwaiter().GetResult(),
itemsCache.GetAllKeys().SelectMany(y => y).Where(a => a == DOKUMENTI_KEY).SelectMany(z => itemsCache.GetObject<List<Dokument>>(z)),
Observable.Return(new List<Dokument>())
);
}
Is there a better way to do this?
Related
One of IEnumerable's overload is:
public static IEnumerable<TResult> Select<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, int, TResult> selector);
In the selector I wish to include the source. I know this sounds counter-intuitive because you provide the source to Select in the first place, but JavaScript has something similar. I would want to use it in a quick situation like this one here:
var greetings = new List<string> { "John", "Keith", "Sarah", "Matt" }.Select((name, index, source) => {
if (name == source.First())
return $"{name} (Todays Winner)";
return name;
});
The above will have an error because Select's selector parameter does not return 3 values. Just the current object, and index. I want it to include the source.
I don't want to first create the list separately and then do .first on it.
Here is how far I've gone with the extension; I'm not sure how to implement it.
public static IEnumerable<TResult> Select<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, int, TResult, IEnumerable<TSource>> selector)
{
//not sure what to put in here, must be missing something simple ;(
}
Update
The above situation is just a made up example. My actual case requires using .Last() not .First() so index won't be useful since we don't know what the last index will be, as opposed to zero being first. Hence my need for the source to be passed back.
This should do it:
public static IEnumerable<TResult> Select<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, int, IEnumerable<TSource>, TResult> selector)
{
using (var enumerator = source.GetEnumerator()) {
for (var i = 0 ; enumerator.MoveNext() ; i++) {
yield return selector(enumerator.Current, i, source);
}
}
}
Note that you have written the wrong type for the selector parameter. It should be Func<TSource, int, IEnumerable<TSource>, TResult>, not Func<TSource, int, TResult, IEnumerable<TSource>>.
If you just want to check if an element is the first, why not just check index == 0?
var greetings = new List<string> { "John", "Keith", "Sarah", "Matt" }.Select((name, index, source) => {
if (index == 0)
return $"{name} (Todays Winner)";
return name;
});
This should work:
public static IEnumerable<TResult> Select<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, int, IEnumerable<TSource>, TResult> selector)
{
int index = 0;
foreach(var item in source)
{
yield return selector(item, index, source);
index++;
}
}
I'm trying to make a IDictionary<TKey, List<TSource>> class where I can decide the key by a func. I have managed to build the class with arrays like this (functions are simplified for examples sake):
private List<TSource> Filter(TKey key, Func<TSource, TKey> keySelector)
{
IQueryable<TSource> source = GetSource(); // It's there
IQueryable<TSource> filtered = source.Where(
x => keySelector.Invoke(x).Equals(key)
);
return filtered.ToList();
}
But this works only with arrays and such, not with linq-to-sql. I understand this can be done with expressions, but thats mostly beyond me. I have googled and come up with the following functions:
private List<TSource> Filter(TKey key, Expression<Func<TSource, TKey>> keySelector)
{
IQueryable<TSource> source = GetSource();
Func<TSource, bool> compiledKeyFilter = GetFilter(keySelector);
IEnumerable<TSource> filtered = source.Where(compiledKeyFilter);
return filtered.ToList();
}
private Func<TSource, bool> GetFilter(Expression<Func<TSource, TKey>> expr)
{
if (this.filter == null)
{
var invokedExpr = Expression.Invoke(expr, expr.Parameters.Cast<Expression>());
var lamda = Expression.Lambda<Func<TSource, bool>>(
Expression.Call(expr.Body, typeof(TKey).GetMethod("Equals", new[] { typeof(TKey) }), invokedExpr),
expr.Parameters
);
this.filter = lamda.Compile();
}
return this.filter;
}
This currently returns all rows in the source. The expression should be reusable, not just for one-time execution. I found the following from SO (Dynamically generated lookup key for IQueryable), what I'm doing is quie similar. It works, but I'm unable to combine it with the compiled approach:
private Expression<Func<TSource, bool>> MakeFilterExpression(TKey key)
{
var param = Expression.Parameter(typeof(TSource));
return Expression.Lambda<Func<TSource, bool>>(
Expression.Equal(
Expression.Invoke(keySelector, param),
Expression.Constant(key)
),
param
);
}
So I'm trying to come up with a class that I could use like this:
// Inside MyCache there would be something close to this:
class MyCache
{
private Func<TSource, bool> filter;
public MyCache(Expression<Func<TSource, TKey>> func)
{
this.filter = MakeFilter(func);
}
private Func<TSource, bool> MakeFilter(Expression<Func<TSource, TKey>> func)
{
// magic
}
public List<TSource> GetByKey(TKey key)
{
return GetSource().Where(this.filter(key)).ToList();
}
}
// This is my class where I give my func to determine the key in ctor.
var cache = new MyCache<MySource>(x => x.myField);
var list1 = cache.GetByKey(3); // Now I have list to iterate.
var list2 = cache.GetByKey(4); // Here's another list.
Is it even possible to compile that into a reusable function? Help?!
If I understand your question correctly, then you are trying to come up with a version of the Filter method that works with LINQ-to-SQL queries given an Expression<Func<TSource, TKey>> key selector.
You can use the LINQKit library to help you with that. It allows you to invoke one expression from another one and then "expand" the result expression.
Here is how you would code your Filter method with the help of LINQKit:
private static List<TSource> Filter<TSource,TKey>(
TKey key,
Expression<Func<TSource, TKey>> keySelector)
{
IQueryable<TSource> source = GetSource<TSource>(); // It's there
Expression<Func<TSource, bool>> predicate = x => keySelector.Invoke(x).Equals(key);
//LINQ-to-SQL cannot translate `Invoke` into SQL. However, after invoking this line
//LINQKit would flatten the expression so that it contains something like
//x => x.Age.Equals(5) (for a keySelector of value x => x.Age and key of value 5)
predicate = predicate.Expand();
IQueryable<TSource> filtered = source.Where(predicate);
return filtered.ToList();
}
I have a generic linked list, currently made up of ints, and I want to sort them by ascending order by default, and then switch a boolean to sort them by descending values. How would I go about doing this?
Assuming your linked list implements IEnumerable<T> (which it probably should!), you can just use the LINQ functions OrderBy and OrderByDescending.
For ints, the default comparer is fine, so you would just write:
bool ascending = true;
var orderedEnumerable = ascending ? collection.OrderBy(x => x) : collection.OrderByDescending(x => x);
Or, with a function and default args:
IOrderedEnumerable<int> GetOrderedNumbers(bool ascending = true)
{
return ascending ? collection.OrderBy(x => x) : collection.OrderByDescending(x => x);
}
MSDN for OrderBy: http://msdn.microsoft.com/en-us/library/vstudio/bb534966(v=vs.100).aspx
If you use the .NET's LinkedList<T> that, in its turn, implements IEnumerable<T> you can use some of these solutions:
This extension method returns a sorted copy of type LinkedList<T>
public static LinkedList<TSource> SortedAscending<TSource, TKey>(
this LinkedList<TSource> source,
Func<TSource, TKey> keySelector)
{
LinkedList<TSource> tempLinkedList = new LinkedList<TSource>();
IEnumerable<TSource> orderedEnumerable = source.OrderBy(keySelector).AsEnumerable();
orderedEnumerable.ForEach(value => tempLinkedList.AddLast(value));
return tempLinkedList;
}
This extension method sorts the source of type LinkedList<T>
public static void SelfSortAscending<TSource, TKey>(
this LinkedList<TSource> source,
Func<TSource, TKey> keySelector)
{
LinkedList<TSource> tempLinkedList = new LinkedList<TSource>(source);
source.Clear();
IEnumerable<TSource> orderedEnumerable = tempLinkedList.OrderBy(keySelector).AsEnumerable();
orderedEnumerable.ForEach(value => source.AddLast(value));
}
Extension methods for descending ordering you can find at:
LinkedListHelper (GitHub link)
By the way, .ForEach() you could implement like this:
public static void ForEach<T>(this IEnumerable<T> source, Action<T> action)
{
if (action == null)
throw new ArgumentNullException(nameof(action));
foreach (T element in source)
action(element);
}
I want to run through an IObservable<T> looking for an element that matches a predicate, and if not found, return the last element of the IObservable<T>. I don't want to have to store the entire contents of the IObservable<T>, and I don't want to loop through the IObservable twice, so I've set up an extension method
public static class ObservableExtensions
{
public static IObservable<T> FirstOrLastAsync<T>(this IObservable<T> source, Func<T, bool> pred)
{
return Observable.Create<T>(o =>
{
var hot = source.Publish();
var store = new AsyncSubject<T>();
var d1 = hot.Subscribe(store);
var d2 = hot.FirstAsync(x => pred(x)).Amb(store).Subscribe(o);
var d3 = hot.Connect();
return new CompositeDisposable(d1, d2, d3);
});
}
public static T FirstOrLast<T>(this IObservable<T> source, Func<T, bool> pred)
{
return source.FirstOrLastAsync(pred).Wait();
}
}
The Async method creates a hot observable from a potentially cold one passed in. It subscribes an AsyncSubject<T> to remember the last element, and an IObservable<T> that looks for the element. It then takes the first element from either of those IObservable<T>s, which ever returns a value first via .Amb (AsyncSubject<T> doesn't return a value until it gets an .OnCompleted message).
My questions are the following:
Can this be written better or more concisely using different Observable methods?
Do all of those disposables need to be included in the CompositeDisposable?
When the hot observable is completed without finding a matching element, is there a race condition between FirstAsync throwing an exception, and the AsyncSubject propagating its value?
If so, do I need to change the line to:
var d2 = hot.Where(x => pred(x)).Take(1).Amb(store).Subscribe(o);
I'm pretty new to RX, and this is my first extension on IObservable.
EDIT
I ended up going with
public static class ObservableExtensions
{
public static IObservable<T> FirstOrLastAsync<T>(this IObservable<T> source, Func<T, bool> pred)
{
var hot = source.Publish().RefCount();
return hot.TakeLast(1).Amb(hot.Where(pred).Take(1).Concat(Observable.Never<T>()));
}
public static T FirstOrLast<T>(this IObservable<T> source, Func<T, bool> pred)
{
return source.FirstOrLastAsync(pred).First();
}
}
You could Amb the two cases you want together.
If your source observable is cold, you can do a Publish|Refcount.
public static IObservable<T> FirstOrLast<T>(this IObservable<T> source, Func<T, bool> predicate)
{
return source.TakeLast(1).Amb(source.Where(predicate).Take(1));
}
Test:
var source = Observable.Interval(TimeSpan.FromSeconds(0.1))
.Take(10)
.Publish()
.RefCount();
FirstOrLast(source, i => i == 5).Subscribe(Console.WriteLine); //5
FirstOrLast(source, i => i == 11).Subscribe(Console.WriteLine); //9
I've tried to produce a "simpler" query that works and so far nothing.
If I stick with your basic structure I can offer a slight improvement. Try this:
public static IObservable<T> FirstOrLastAsync<T>(
this IObservable<T> source, Func<T, bool> pred)
{
return Observable.Create<T>(o =>
{
var hot = source.Publish();
var store = new AsyncSubject<T>();
var d1 = hot.Subscribe(store);
var d2 =
hot
.Where(x => pred(x))
.Concat(store)
.Take(1)
.Subscribe(o);
var d3 = hot.Connect();
return new CompositeDisposable(d1, d2, d3);
});
}
It's not hugely better, but I like it better than using Amb. It's just a tad cleaner I think.
I want to get the distinct values in a list, but not by the standard equality comparison.
What I want to do is something like this:
return myList.Distinct( (x, y) => x.Url == y.Url );
I can't, there's no extension method in Linq that will do this - just one that takes an IEqualityComparer.
I can hack around it with this:
return myList.GroupBy( x => x.Url ).Select( g => g.First() );
But that seems messy. It also doesn't quite do the same thing - I can only use it here because I have a single key.
I could also add my own:
public static IEnumerable<T> Distinct<T>(
this IEnumerable<T> input, Func<T,T,bool> compare )
{
//write my own here
}
But that does seem rather like writing something that should be there in the first place.
Anyone know why this method isn't there?
Am I missing something?
It's annoying, certainly. It's also part of my "MoreLINQ" project which I must pay some attention to at some point :) There are plenty of other operations which make sense when acting on a projection, but returning the original - MaxBy and MinBy spring to mind.
As you say, it's easy to write - although I prefer the name "DistinctBy" to match OrderBy etc. Here's my implementation if you're interested:
public static IEnumerable<TSource> DistinctBy<TSource, TKey>
(this IEnumerable<TSource> source,
Func<TSource, TKey> keySelector)
{
return source.DistinctBy(keySelector,
EqualityComparer<TKey>.Default);
}
public static IEnumerable<TSource> DistinctBy<TSource, TKey>
(this IEnumerable<TSource> source,
Func<TSource, TKey> keySelector,
IEqualityComparer<TKey> comparer)
{
if (source == null)
{
throw new ArgumentNullException("source");
}
if (keySelector == null)
{
throw new ArgumentNullException("keySelector");
}
if (comparer == null)
{
throw new ArgumentNullException("comparer");
}
return DistinctByImpl(source, keySelector, comparer);
}
private static IEnumerable<TSource> DistinctByImpl<TSource, TKey>
(IEnumerable<TSource> source,
Func<TSource, TKey> keySelector,
IEqualityComparer<TKey> comparer)
{
HashSet<TKey> knownKeys = new HashSet<TKey>(comparer);
foreach (TSource element in source)
{
if (knownKeys.Add(keySelector(element)))
{
yield return element;
}
}
}
But that seems messy.
It's not messy, it's correct.
If you want Distinct Programmers by FirstName and there are four Amy's, which one do you want?
If you Group programmers By FirstName and take the First one, then it is clear what you want to do in the case of four Amy's.
I can only use it here because I have a single key.
You can do a multiple key "distinct" with the same pattern:
return myList
.GroupBy( x => new { x.Url, x.Age } )
.Select( g => g.First() );
Jon, your solution is pretty good. One minor change though. I don't think we need EqualityComparer.Default in there. Here is my solution (ofcourse the starting point was Jon Skeet's solution)
public static IEnumerable<T> DistinctBy<T, TKey>(this IEnumerable<T> source, Func<T, TKey> keySelector)
{
//TODO All arg checks
HashSet<TKey> keys = new HashSet<TKey>();
foreach (T item in source)
{
TKey key = keySelector(item);
if (!keys.Contains(key))
{
keys.Add(key);
yield return item;
}
}
}
Using AmyB's answer, I've written a small DistinctBy extension method, to allow a predicate to be passed:
/// <summary>
/// Distinct method that accepts a perdicate
/// </summary>
/// <typeparam name="TSource">The type of the t source.</typeparam>
/// <typeparam name="TKey">The type of the t key.</typeparam>
/// <param name="source">The source.</param>
/// <param name="predicate">The predicate.</param>
/// <returns>IEnumerable<TSource>.</returns>
/// <exception cref="System.ArgumentNullException">source</exception>
public static IEnumerable<TSource> DistinctBy<TSource, TKey>
(this IEnumerable<TSource> source,
Func<TSource, TKey> predicate)
{
if (source == null)
throw new ArgumentNullException("source");
return source
.GroupBy(predicate)
.Select(x => x.First());
}
You can now pass a predicate to group the list by:
var distinct = myList.DistinctBy(x => x.Id);
Or group by multiple properties:
var distinct = myList.DistinctBy(x => new { x.Id, x.Title });