I am trying to find a generic way to assign values to a property dictated by a lambda expression, look at the example code below, how would the signature for the ConverToEntities method look and how would it be called?
static void Main()
{
List<long> ids = new List<long> {1, 2, 3};
//Non generic way
List<Data> dataItems = ids.ConvertToDataItems();
//Generic attempt!!
List<Data> differntDataItems =
ids.ConvertToEntities<Data>( p => p.DataId );
}
public class Data
{
public long DataId;
public string Name;
}
public static class ExtensionMethods
{
public static List<Data> ConvertToDataItems(this List<long> dataIds)
{
return dataIds.Select(p => new Data { DataId = p }).ToList();
}
public static List<T> ConvertToEntities<TProp>(
this List<long> entities, Func<TProp> lambdaProperty )
{
return entities.Select(p => new T {lambdaProperty} ).ToList();
}
}
Ok. The closest I could get was this :
class Program
{
static void Main(string[] args)
{
List<long> ids = new List<long> { 1, 2, 3 };
//Non generic way
List<Data> dataItems = ids.ConvertToDataItems();
//Generic attempt!!
Func<long, Data> selector = (p => new Data { DataId = p });
List<Data> differntDataItems = ids.ConvertToEntities<Data>(selector);
}
}
public class Data
{
public long DataId;
public string Name;
}
public static class ExtensionMethods
{
public static List<Data> ConvertToDataItems(this List<long> dataIds)
{
return dataIds.Select(p => new Data { DataId = p }).ToList();
}
public static List<TProp> ConvertToEntities<TProp>(this List<long> entities, Func<long, TProp> selector)
{
return entities.Select(selector).ToList();
}
}
This works.
I have the feeling you got urself a little confused with what you actually want as the return type. It would be cool to be able to specify what we want in the method call or smth. For example:
public static List<TProp> ConvertToEntities<T, TProp>(List<T> entities, Func<T, TProp> selector)
{
return entities.Select(selector).ToList();
}
This provides us more flexibility on the return type. But since we are doing this using extensions, I assume this is impractical because we need to know what type we are extending:
this List<long> entities,
Nice question.
EDIT Code suggestion fix.
You can do something like this, but it's not as simple or nice. The lambda p => p.DataId gives you the get accessor of the property. You could use Expressions to get the setter, but it's probably better to use the setter directly in the lambda:
List<Data> differntDataItems =
ids.ConvertToEntities<long, Data>((p, i) => p.DataId = i);
The implementation would look like this:
public static List<T> ConvertToEntities<TProp, T>(
this List<TProp> dataIds, Action<T, TProp> lambdaProperty)
where T : new()
{
return dataIds.Select(
p =>
{
var result = new T();
lambdaProperty(result, p);
return result;
}).ToList();
}
I believe #Zortkun is right about the return type. Try the followin:
public static List<TProp> ConvertToEntities<TProp>(
this List<long> entities, Func<long, TProp> lambdaProperty )
{
return entities.Select(lambdaProperty).ToList();
}
and you would call it as follows:
ids.ConvertToEntities<Data>( p => new Data { DataId = p } );
Related
essentially, exactly as in this question, but in c#.
I want to have something like this:
public IList<double> DoSomething(IList<string> strings) {
//something
}
But i'd rather have it typed such that the return value is known ahead of time so that i don't have to cast or anything outside the function. How do i do this?
Is there a generic way,
public ListType<double> DoSomething<ListType>(ListType<string> strings) where ListType : ???{
to set this up?
C# does not allow exactly what you want, but the closest thing would be:
public TList2 DoSomething<TList1, TList2>(TList1 strings)
where TList1 : IEnumerable<string>
where TList2 : ICollection<decimal>, new()
{
var result = new TList2();
foreach (var s in strings)
{
result.Add(decimal.Parse(s));
}
return result;
}
and an example:
var input = new HashSet<string>(new[] { "1", "2", "3"});
List<decimal> output = DoSomething<HashSet<string>, List<decimal>>(input);
You can use Linq to do this.
For instance if you just want to parse to double you would do:
List<double> result = strings.Select(double.Parse).ToList()
And you can send in any other method instead of double.Parse:
List<double> result = strings.Select(DoSomethingWithOneItem).ToList()
double DoSomethingWithOneItem(string item) {
//your conversion logic
}
Unfortunately there is no relation between for instance IList<TInput> and IList<TOutput> that can be used to help here, so you will need to specify both the input and output list type, which becomes a bit cumbersome in the general form:
IList<string> strings = new List<string> {"1.1", "2.2", "3.3"};
IList<decimal> result = strings.ConvertToSameListType((Func<string, decimal>)decimal.Parse, () => new List<decimal>());
public static class EnumerableExtensioncGeneralVersion
{
public static TOutputList ConvertToSameListType<TInputList, TOutputList, TInput, TOutput>(this TInputList source, Func<TInput, TOutput> itemConversion, Func<TOutputList> outputListConstructor)
where TInputList : IEnumerable<TInput>
where TOutputList : ICollection<TOutput>
{
TOutputList result = outputListConstructor();
foreach (TOutput convertedItem in source.Select(itemConversion))
{
result.Add(convertedItem);
}
return result;
}
}
Though you can make it much nicer to use your conversions if you just specify one extension method for each of your favourite collection types that you want to support:
//Seting up inputs
IList<string> strings = new List<string> {"1.1", "2.2", "3.3"};
IEnumerable<string> enumerableStrings = strings.Select(x => x);
ObservableCollection<string> observableStrings = new ObservableCollection<string>(strings);
//Converting
IList<decimal> resultList = strings.Convert(decimal.Parse);
IEnumerable<decimal> resultEnumerable = enumerableStrings.Convert(decimal.Parse);
ObservableCollection<decimal> observableResult = observableStrings.Convert(decimal.Parse);
public static class EnumerableExtensions
{
public static IList<TOutput> Convert<TInput, TOutput>(this IList<TInput> source, Func<TInput, TOutput> itemConversion)
{
return source.Select(itemConversion).ToList();
}
public static IEnumerable<TOutput> Convert<TInput, TOutput>(this IEnumerable<TInput> source, Func<TInput, TOutput> itemConversion)
{
return source.Select(itemConversion);
}
public static ObservableCollection<TOutput> Convert<TInput, TOutput>(this ObservableCollection<TInput> source, Func<TInput, TOutput> itemConversion)
{
return new ObservableCollection<TOutput>(source.Select(itemConversion));
}
}
So, say I have something like the following:
public class Element
{
public int ID;
public int Type;
public Properties prorerty;
...
}
and
public class Properties
{
public int Id;
public string Property;
...
}
and I have a list of these:
List Elements = new List();
What would be the cleanest way to get a list of all distinct values in the prorerty column in Element class? I mean, I could iterate through the list and add all values that aren't duplicates to another list of strings, but this seems dirty and inefficient. I have a feeling there's some magical Linq construction that'll do this in one line, but I haven't been able to come up with anything.
var results = Elements.Distinct();
Note: you will have to override .Equals and .GetHashCode()
public class Element : IEqualityComparer<Element>
{
public bool Equals(Element x, Element y)
{
if (x.ID == y.ID)
{
return true;
}
else
{
return false;
}
}
}
public int GetHashCode(Element obj)
{
return obj.ID.GetHashCode();
}
Isn't simpler to use one of the approaches shown below :) ? You can just group your domain objects by some key and select FirstOrDefault like below.
This is a copy of my answer on similar question here:
Get unique values - original answer
More interesting option is to create some Comparer adapter that takes you domain object and creates other object the Comparer can use/work with out of the box. Base on the comparer you can create your custom linq extensions like in sample below. Hope it helps :)
[TestMethod]
public void CustomDistinctTest()
{
// Generate some sample of domain objects
var listOfDomainObjects = Enumerable
.Range(10, 10)
.SelectMany(x =>
Enumerable
.Range(15, 10)
.Select(y => new SomeClass { SomeText = x.ToString(), SomeInt = x + y }))
.ToList();
var uniqueStringsByUsingGroupBy = listOfDomainObjects
.GroupBy(x => x.SomeText)
.Select(x => x.FirstOrDefault())
.ToList();
var uniqueStringsByCustomExtension = listOfDomainObjects.DistinctBy(x => x.SomeText).ToList();
var uniqueIntsByCustomExtension = listOfDomainObjects.DistinctBy(x => x.SomeInt).ToList();
var uniqueStrings = listOfDomainObjects
.Distinct(new EqualityComparerAdapter<SomeClass, string>(x => x.SomeText))
.OrderBy(x=>x.SomeText)
.ToList();
var uniqueInts = listOfDomainObjects
.Distinct(new EqualityComparerAdapter<SomeClass, int>(x => x.SomeInt))
.OrderBy(x => x.SomeInt)
.ToList();
}
Custom comparer adapter:
public class EqualityComparerAdapter<T, V> : EqualityComparer<T>
where V : IEquatable<V>
{
private Func<T, V> _valueAdapter;
public EqualityComparerAdapter(Func<T, V> valueAdapter)
{
_valueAdapter = valueAdapter;
}
public override bool Equals(T x, T y)
{
return _valueAdapter(x).Equals(_valueAdapter(y));
}
public override int GetHashCode(T obj)
{
return _valueAdapter(obj).GetHashCode();
}
}
Custom linq extension (definition of DistinctBy extension method):
// Embed this class in some specific custom namespace
public static class DistByExt
{
public static IEnumerable<T> DistinctBy<T,V>(this IEnumerable<T> enumerator,Func<T,V> valueAdapter)
where V : IEquatable<V>
{
return enumerator.Distinct(new EqualityComparerAdapter<T, V>(valueAdapter));
}
}
Definition of domain class used in test case:
public class SomeClass
{
public string SomeText { get; set; }
public int SomeInt { get; set; }
}
var props = Elements.Select(x => x.Properties).Distinct();
And make sure you overridden .Equals() and .GetHashCode() methods.
Or if you need direct strings from Properties:
var props = Elements
.Select(x => x.Properties != null ? x.Properties.Property : null)
.Distinct();
If you need the string fields on the Properties field, and if you know the Properties field prorerty is never null, just use
IEnumerable<string> uniqueStrings = Elements
.Select(e => e.prorerty.Property).Distinct();
If there's a chance prorerty can be null, handle that situation in the lambda.
This will use the default equality comparer for String which is an ordinal comparison independent of culture and case-sensitive.
my working example from LINQPad (C# Program)
void Main()
{
var ret = new List<Element>();
ret.Add(new Element(){ID=1});
ret.Add(new Element(){ID=1});
ret.Add(new Element(){ID=2});
ret = ret.GroupBy(x=>x.ID).Select(x=>x.First()).ToList();
Console.WriteLine(ret.Count()); // shows 2
}
public class Element
{
public int ID;
public int Type;
public Properties prorerty;
}
public class Properties
{
public int Id;
public string Property;
}
I want to compare two lists, based on a filter expression; not sure how to construct the lambda expression for the generic method; Please refer to the code below; or is there an easier way via an intersect in LINQ?
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
Data d1 = new Data {Id = 1, Name = "One"};
Data d2 = new Data { Id = 2, Name = "Two" };
Data d3 = new Data { Id = 3, Name = "Three" };
Data d4 = new Data { Id = 1, Name = "One" };
Data d5 = new Data { Id = 2, Name = "Two" };
Data d6 = new Data { Id = 4, Name = "Four" };
List<Data> original = new List<Data> {d1, d2, d3};
List<Data> filterItems = new List<Data> {d4, d5, d6};
List<Data> result = original.FilterDataList(filterItems);
//How to call this method?
List<Data> genericCall = original.FilterList<Data>(filterItems, data => data.Id ?????????????)
}
}
public class Data
{
public long Id;
public string Name;
}
public static class Extensions
{
public static List<Data> FilterDataList(this List<Data> sourceList, List<Data> filterOutItems)
{
return sourceList.Where(p => filterOutItems.All(l => l.Id != p.Id)).ToList();
}
public static List<T> FilterList<T>(this List<T> sourceList, List<T> filterOutItems, Func<T, bool> filterExpression)
{
return sourceList.Where(p => filterOutItems.All(filterExpression)).ToList();
}
}
}
Thanks to everyone for pointing the LINQ Except extension out, here is my end solution
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
Data d1 = new Data {Id = 1, Name = "One"};
Data d2 = new Data { Id = 2, Name = "Two" };
Data d3 = new Data { Id = 3, Name = "Three" };
Data d4 = new Data { Id = 1, Name = "One" };
Data d5 = new Data { Id = 2, Name = "Two" };
List<Data> original = new List<Data> {d1, d2, d3};
List<Data> filterItems = new List<Data> {d4, d5, d6};
List<Data> datas = original.Except(filterItems, (x, y) => x.Id == y.Id).ToList();
}
}
public class Data
{
public long Id;
public string Name;
}
public static class EnumerableExtension
{
public static IEnumerable<T> Except<T>(this IEnumerable<T> listA, IEnumerable<T> listB,
Func<T, T, bool> lambda)
{
return listA.Except(listB, new Comparer<T>(lambda));
}
public static IEnumerable<T> Intersect<T>(this IEnumerable<T> listA, IEnumerable<T> listB,
Func<T, T, bool> lambda)
{
return listA.Intersect(listB, new Comparer<T>(lambda));
}
}
public class Comparer<T> : IEqualityComparer<T>
{
private readonly Func<T, T, bool> _expression;
public Comparer(Func<T, T, bool> lambda)
{
_expression = lambda;
}
public bool Equals(T x, T y)
{
return _expression(x, y);
}
public int GetHashCode(T obj)
{
/*
If you just return 0 for the hash the Equals comparer will kick in.
The underlying evaluation checks the hash and then short circuits the evaluation if it is false.
Otherwise, it checks the Equals. If you force the hash to be true (by assuming 0 for both objects),
you will always fall through to the Equals check which is what we are always going for.
*/
return 0;
}
}
}
If I understand your question correctly, FilterList is a generic version of FilterDataList where you are passing in the lambda as a parameter. In that case you would call the method as follows:
List<Data> genericCall = original.FilterList<Data>(filterItems, (x, y) => x.Id != y.Id);
If you want to use Except as #ivancho and #perelman have suggested you could use a method like this:
public static class EnumerableExtension
{
public static IEnumerable<T> Except<T>(this IEnumerable<T> listA, IEnumerable<T> listB,
Func<T, T, bool> lambda)
{
return listA.Except(listB, new Comparer<T>(lambda));
}
public static IEnumerable<T> Intersect<T>(this IEnumerable<T> listA, IEnumerable<T> listB,
Func<T, T, bool> lambda)
{
return listA.Intersect(listB, new Comparer<T>(lambda));
}
}
You would then call it as follows:
original.Except<Data>(filterItems, (x, y) => x.Id != y.Id);
What is your desired output? Did you try the first result in https://www.google.com/search?q=linq+intersect ? It seems like you should go through the Enumerable documentation - you are using .All where you most likely mean .Any, and just in general it would give you a better idea of what is possible with LINQ.
I am not clear what you are trying to do. Your FilterDataList appears to be the same as Except().ToList(). The .Where in your FilterList does not use p (the argument to the lambda), so I am unclear what you want to do with the filter expression. Maybe you are looking for using a different IEqualityComparer with Except() which you would have to define as a separate class.
I want to make small framework with i could simply invoke webservices on many computers that have webservice.
So, i have i.e five computers with webservices.
Each ws provides 2 functions (could be more, but this is example):
DataFormat[] GetXData(int)
Something[] GetYData(string, int).
Invoking service now looks like this:
ServiceClient wsc;
DataFormat[] data = wsc.GetXData(5);
I plan interface of framework like this:
MultiWebservice mws;
DataFormat[] data = mws.BroadcastQuery( wsc.GetXData(5) );
As can see, i wish to inject function with iam interested to fire on every ws. And return merged data (merging is not subject of post. i handle it myself)
I need a help how use C# to make this elegant, generic and if it isn't necessary,
without many overloading of function because i don't want make new overloadings for each different return type or
each function in ws.
Please, give me advice. Maybe this interface is wrong and could be better.
To give an answer similar to Thomas Li's, but using a generic type parameter for the methods, to allow any return type:
public class WSClient {
public int GetPower (int var) { return var * var; }
public int[] GetDuplicatePowers (int var) {
return new[] { GetPower(var), GetPower (var) };
}
}
public class Multiplexer<T> {
IList<T> _sources;
public Multiplexer (IEnumerable<T> sources) {
_sources = new List<T> (sources);
}
public IEnumerable<TResult> Call<TResult> (Func<T, TResult> func) {
return _sources.Select (s => func(s));
}
public IEnumerable<TResult> AggregateCall<TResult> (Func<T, IEnumerable<TResult>> func) {
return _sources.SelectMany (s => func(s));
}
}
public class Test {
public static void Main (string[] args) {
var m = new Multiplexer<WSClient> (new[] { new WSClient (), new WSClient () });
var powers = m.Call (c => c.GetPower (2));
var agg_powers = m.AggregateCall (c => c.GetDuplicatePowers (2));
}
}
Not sure if this helps but you can try tweaking this:
public class WebServiceClient
{
public int[] GetXData(int intVar)
{
return new int[] { intVar, intVar };
}
}
public class BoardcastingWebServiceCleint
{
public int[] BroadcastQuery(Func<WebServiceClient, int[]> webServiceCall)
{
List<WebServiceClient> clients = new List<WebServiceClient>();
List<int> allResults = new List<int>();
foreach (WebServiceClient client in clients)
{
int[] result = webServiceCall.Invoke(client);
allResults.AddRange(result);
}
return allResults.ToArray();
}
}
static void Main(string[] args)
{
BoardcastingWebServiceCleint bwsc = new BoardcastingWebServiceCleint();
bwsc.BroadcastQuery((client) => { return client.GetXData(5); });
}
I am creating a Distinct extension method where I can pass in the criteria like the following.
persons.Distinct(p => p.Name);
I got the code from the web but I am having a hard time understanding the purpose of Func<T, TResult>. Also, when I say p => p.Name am I sending the String Name or am I sending the complete Person object? Here is the new Distinct method:
public static class ExtensionMethods
{
public static IEnumerable<T> Distinct<T>(
this IEnumerable<T> list, Func<T,object> checker)
{
return list.Distinct(new GenericComparer<T>(checker));
}
}
public class GenericComparer<T> : IEqualityComparer<T>
{
private Func<T, object> _checker;
public GenericComparer(Func<T,object> checker)
{
_checker = checker;
}
public bool Equals(T x, T y)
{
return _checker(x).Equals(_checker(y));
}
public int GetHashCode(T obj)
{
return _checker(obj).GetHashCode();
}
}
And here is the usage:
static void Main(string[] args)
{
var persons = new List<Person>()
{
new Person() { Id = 1, Name = "Mary"},
new Person() {Id = 2, Name="John"},
new Person() { Id = 3, Name = "Mary"}
};
var uniquePersons = persons.Distinct(p => p.Name);
foreach(var person in uniquePersons)
{
Console.WriteLine(person.Name);
}
}
When you do this:
persons.Distinct(p => p.Name);
You're basically creating a function on the fly (using lambda expressions), that looks like this:
string theFunction(Person p)
{
return p.Name;
}
This is a function that fits the signature of a Func<Person,String> delegate. The Distinct method can take a delegate (basically a function pointer) which it uses to determine whether or not an element is distinct - in your case, only unique strings (returned by the function above) will be considered "distinct" elements. This delegate is run on each element of your "persons" enumerable, and the results of those functions are used. It then creates a sequence (IEnumerable<Person>) from those elements.
Func<T, TResult>
defines a function that accepts one parameter (of type T) and returns an object (of type TResult).
In your case, if you want a function that takes a Person object and returns a string...you'd want
Func<Person, string>
which is the equivalent of:
string Function(Person p)
{
return p.Name;
}
You are getting back the distinct People, under the assumption that two People are the same if they have the same name
If you want a distinct set of names, you can use this:
IEnumerable<String> names = persons.Select(p => p.Name).Distinct();