DistinctBy not recognized as method - c#

Maybe I am missing an using? (I have using System.Linq).
With Distinct no problem.
This is my command that i want to add DistinctBy:
List<Capture> list = db.MyObject.Where(x => x.prop == "Name").ToList();

You can add a extension method
public static IEnumerable<T> DistinctBy<T, TKey>(this IEnumerable<T> items, Func<T, TKey> property)
{
return items.GroupBy(property).Select(x => x.First());
}
and You can use it like
List<Capture> list = db.MyObject.Where(x => x.prop == "Name")
.DistinctBy(y=> y.prop )
.ToList();
OR, You can use DistincyBy provided through MoreLinq.

Another example:
public static class ExtensionMethods
{
public static IEnumerable<TSource> DistinctBy<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector)
{
var seenKeys = new HashSet<TKey>();
foreach (TSource element in source)
{
if (seenKeys.Add(keySelector(element)))
{
yield return element;
}
}
}
}

MyList.GroupBy(a=>a.item).select(a=>a.FirstOrDefault()).ToList();//use this
//is equal to
MyList.DistinctBy(a=>a.item).ToList();//its not work with linq

Now in dotnet 6.0 there is a built in DistinctBy extension in System.Linq namespace
https://learn.microsoft.com/en-us/dotnet/api/system.linq.enumerable.distinctby?view=net-6.0&viewFallbackFrom=net-5.0

Related

Extension of IEnumerable's Select to include the source in the selector

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

Create an extension method derived from ToDictionary()

I want to create an extension method derived from ToDictionary(). Currently to reach the desired result, I do this:
ObjectAttributes = model.ObjectAttributes.ToDictionary(
oa => oa.Attribute.Name, oa => oa.ToWrapper<ObjectAttributeWrapper>());
So I use the following ToDictionary signature:
public static Dictionary<TKey, TElement> ToDictionary<TSource, TKey, TElement>(
this IEnumerable<TSource> source,
Func<TSource, TKey> keySelector,
Func<TSource, TElement> elementSelector);
I wish to know if it is possible to do this?
ObjectAttributes = model.ObjectAttributes.ToDictionaryWrapper<ObjectAttributeWrapper>(
oa => oa.Attribute.Name);
here is the current implementation but it does not work obviously:
public static Dictionary<TKey, TWrapper> ToDictionaryWrapper<TWrapper, TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector) where TSource : BaseModel where TWrapper : IBaseWrapper
{
return source.ToDictionary(keySelector, model => model.ToWrapper<TWrapper>());
}
EDIT :
Implementation of ToWrapper():
public static TWrapper ToWrapper<TWrapper>(this BaseModel model) where TWrapper : IBaseWrapper
{
if (model == null)
return default;
var type = typeof(TWrapper);
if (_wrapperParents.ContainsKey(type) && _wrapperParents[type].Id == model.Id)
return (TWrapper)_wrapperParents[type];
else
return (TWrapper)GetConstructor<TWrapper>().Invoke(new object[] { model });
}
public static IEnumerable<TWrapper> ToListWrapper<TWrapper>(this IEnumerable models) where TWrapper : IBaseWrapper
{
var _models = models as IEnumerable<BaseModel>;
if (_models == null)
return default;
return _models.Select(model => model.ToWrapper<TWrapper>());
}
Generally, for a single list of generic constraints, you can only provide all arguments explicitely or have all arguments resolved automatically. You can't have partially provided constraints.
So your options are the following (I assume TWrapper would have a property WrappedObject):
Provide all parameters as is
Reduce the number of generic parameters (you can even make the key type non-generic if its always the same)
// signature
Dictionary<TKey, TWrapper> ToDictionaryWrapper<TWrapper, TKey>(
this IEnumerable<BaseModel> source,
Func<BaseModel, TKey> keySelector)
where TWrapper : IBaseWrapper;
// call
model.ObjectAttributes
.ToDictionaryWrapper<ObjectAttributeWrapper, string>(oa => oa.Attribute.Name);
Split your function calls in two parts, where one part is explicit and the other implicit
// signature
IEnumerable<TWrapper> Wrap<TWrapper>(this IEnumerable<BaseModel> source)
where TWrapper : IBaseWrapper;
Dictionary<TKey, TWrapper> ToDictionaryWrapper<TWrapper, TKey>(
this IEnumerable<TWrapper> source,
Func<BaseModel, TKey> keySelector)
where TWrapper : IBaseWrapper;
// call
model.ObjectAttributes
.Wrap<ObjectAttributeWrapper>()
.ToDictionaryWrapper(oa => oa.Attribute.Name);
Don't bother with the custom ToDictionaryWrapper, just use the Wrap-Function and the Framework ToDictionary
// call (signature for Wrap same as above)
model.ObjectAttributes
.Wrap<ObjectAttributeWrapper>()
.ToDictionary(w => w.WrappedObject.Attribute.Name);

Sorting a linked list in ascending and decending order

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

IEnumerable Extension

I want to make an IEnumerable<TSource> extension that can convert itself to a IEnumerable<SelectListItem>. So far I have been trying to do it this way:
public static
IEnumerable<SelectListItem> ToSelectItemList<TSource, TKey>(this
IEnumerable<TSource> enumerable, Func<TSource, TKey> text,
Func<TSource, TKey> value)
{
List<SelectListItem> selectList = new List<SelectListItem>();
foreach (TSource model in enumerable)
selectList.Add(new SelectListItem() { Text = ?, Value = ?});
return selectList;
}
Is this the right way to go about doing it? If so how do I draw the values from the appropriate values from the Func<TSource, TKey> ?
You're re-inventing the wheel. It is what Enumerable.Select intended for.
EDIT BY #KeithS: To answer the question, if you want this output, you can define an extension method wrapping Enumerable.Select:
public static IEnumerable<SelectListItem> ToSelectItemList<TSource>(
this IEnumerable<TSource> enumerable,
Func<TSource, string> text,
Func<TSource, string> value)
{
return enumerable.Select(x=>new SelectListItem{Text=text(x), Value=value(x));
}
You just need to use the two functions you supply as parameters to extract the text and the value. Assuming both text and value are strings you don't need the TKey type parameter. And there is no need to create a list in the extension method. An iterator block using yield return is preferable and how similar extension methods in LINQ are built.
public static IEnumerable<SelectListItem> ToSelectItemList<TSource>(
this IEnumerable<TSource> enumerable,
Func<TSource, string> text,
Func<TSource, string> value)
{
foreach (TSource model in enumerable)
yield return new SelectListItem { Text = text(model), Value = value(model) };
}
You can use it like this (you need to supply the two lambdas):
var selectedItems = items.ToSelecListItem(x => ..., x => ...);
However, you could just as well use Enumerable.Select:
var selectedItems = items.Select(x => new SelectListItem { Text = ..., Value = ... });
You're on the right path.
Funcs are methods stored in variables, and are invoked like normal methods.
public static IEnumerable<SelectListItem> ToSelectItemList<TSource, TKey>(
this IEnumerable<TSource> enumerable,
Func<TSource, TKey> text,
Func<TSource, TKey> value)
{
List<SelectListItem> selectList = new List<SelectListItem>();
foreach (TSource model in enumerable)
{
selectList.Add(new SelectListItem()
{
Text = text(model),
Value = value(model)
});
}
return selectList;
}
If I might recommend, your Funcs should be Func<TSource, string> as the text and value are strings in the SelectListItem.
Edit Just thought of this...
Also you don't have to create an inner list but can do a yield return instead. Below is my "optimized" version of your method.
public static IEnumerable<SelectListItem> ToSelectItemList<TSource>(
this IEnumerable<TSource> enumerable,
Func<TSource, string> text,
Func<TSource, string> value)
{
foreach (TSource model in enumerable)
{
yield return new SelectListItem()
{
Text = text(model),
Value = value(model)
};
}
}
Here is the reference for yeild return. It allows you to return the your results as an element in an enumerable, constructing your enumerable invisibly (to you).
http://msdn.microsoft.com/en-us/library/9k7k7cf0.aspx
To me it seems like crossing a river to get water. Why not simply use select?
enumerable.Select(item =>
new SelectListItem{
Text = item.SomeProperty,
Value item.SomeOtherProperty
}).ToList();
if you really do want a method then you could do this:
public static
IEnumerable<SelectListItem> ToSelectItemList<TSource, TKey>(this
IEnumerable<TSource> enumerable, Func<TSource, TKey> text,
Func<TSource, TKey> value)
{
return (from item in enumerable
select new SelectListItem{
Text = text(item),
Value = value(item)
}).ToList();
}
A LINQ way of achieving what you want would be:
public static IEnumerable<SelectListItem> ToSelectItemList<TSource, TKey>(
this IEnumerable<TSource> enumerable,
Func<TSource, TKey> textSelector,
Func<TSource, TKey> valueSelector)
{
return from model in enumerable
select new SelectListItem
{
Text = textSelector(model),
Value = valueSelector(model)
};
}
Within the body of your extension method, those two parameters are just delegates, and you can run them like any other function:
selectList.Add(new SelectListItem() { Text = text(model), Value = value(model)});
Other solutions work as well, but I think that the one from Martin Liversage is the best way to do it:
IEnumerable<SelectListItem> selectListItems = items.Select(x =>
new SelectListItem
{
Text = x.TextProperty,
Value = x.ValueProperty
});

Why is there no Linq method to return distinct values by a predicate?

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

Categories