I have a sub class which is 'derived' (is that the right word) from a base class two levels up. I have a list of all the properties in this class (so that includes properties from the parent, and the parent of the parent). What I want is just the properties where the DeclaringType is "CrazyNinjaBadger" (i.e. only the properties from my sub - class).
I've tried this statement:
PropertyInfo[] properties = type.GetProperties().Select(x => x.DeclaringType.ToString() == "CrazyNinjaBadger");
But I just get
"Cannot implicitly convert type
'System.Collections.Generic.IEnumerable' to
'System.Reflection.PropertyInfo[]'.
Please can someone suggest a statement that will work?
Use Where to filter properties, and convert result to array:
PropertyInfo[] properties = type.GetProperties()
.Where(x => x.DeclaringType.ToString() == "CrazyNinjaBadger")
.ToArray();
Also I believe you want to use type name like this x.DeclaringType.Name == "CrazyNinjaBadger". Btw Select operator projects properties to sequence of boolean values in your case. So, your query actually returns IEnumerable<bool> with results of type string comparison to CrazyNinjaBadger.
PropertyInfo[] properties = type.GetProperties().Select(x => x.DeclaringType.ToString() == "CrazyNinjaBadger");
Select(...) returns an implemenetation of IEnumerable<T>. The compiler error is very explicit.
Another point is you want to filter properties. .Select(...) is for projecting an enumerable into another of the same or other type.
For example:
IEnumerable<string> strings = new string[] { "0", "1" };
// Converting the string enumerable to an enumerable of integers:
IEnumerable<int> integers = strings.Select(some => int.Parse(some));
// Also, convert each strings into an anonymous object!
IEnumerable<object> whoKnows = strings.Select(some => new { Value = some });
In order to filter an enumerable you need to use .Where(...).
In the other hand, x.DeclaringType.ToString() == "CrazyNinjaBadger" is correct but it should be x.DeclaringType.Name == "CrazyNinjaBadger" (you don't need to convert the type to string as Type has a property Name).
Finally I'd argue that you don't need to set the result in an array, you can just do this:
IEnumerable<PropertyInfo> properties =
type.GetProperties()
.Where(x => x.DeclaringType.Name == "CrazyNinjaBadger");
PropertyInfo[] properties = type.GetProperties()
.Select(x => x.DeclaringType.ToString() == "CrazyNinjaBadger")
.ToArray();
The ToArray() needs to be added to convert to Array...
You're getting that error because Select() always returns an IEnumerable.
Just Add .ToArray() at the end of the line to make it work.
Add ToArray() at the end of the line
PropertyInfo[] properties = type.GetProperties()
.Select(x => x.DeclaringType.ToString() == "CrazyNinjaBadger")
.ToArray();
You were almost there! Select returns an IEnumerable, but you're trying to set the value of a PropertyInfo array. All you need is an additional call to ToArray and you're there!
PropertyInfo[] properties = type.GetProperties().Select(x => x.DeclaringType.ToString() == "CrazyNinjaBadger").ToArray()
Related
A list should be ordered by a property which is sub property of a object in a list.
pList = pList
.OrderBy(x => x.GetType()
.GetProperty(sortBasedValue)
.GetValue(x, null))
.ToList();
Will sort all Elements in the list, but some subelements have multiple properties.
Already tried subitem.value as sortBasedValue but will not work.
GetProperty takes the name of the property as parameter, but you can't use it to fletch its property's property directly. You would need to chain it.
Enumerable.Empty<object>()
// looks from the naming its a private variable,
//so you might want to call it via , GetProperty("subitem", BindingFlags.NonPublic)
.OrderBy(x =>
{
var subitem = x.GetType().GetProperty("subitem").GetValue(x);
return subitem.GetType().GetProperty("value").GetValue(subitem);
})
.ToList();
If you have the type of the pList, I would suggest you to use a property selector aka Func<TObject, TPropertyToOrderBy> and give it to the OrderBy.
You need to implement a custom IComparer and use the sort method.
The solution is here C# Sort and OrderBy comparison
I am having difficulties understandting what type of statement this is and how to use the .select method.
var lines = System.IO.File.ReadLines(#"c:\temp\mycsvfil3.csv")
.Select(l => new
{
myIdentiafication= int.Parse(l.Split(',')[0].Trim()),
myName= l.Split(',')[1].Trim()
}
).OrderBy(i => i.Id);
any help is appreciated!
The Enumerable.Select method is an extension method for an IEnumerable<T> type. It takes a Func<TSource, TResult> that allows you to take in your IEnumerable<T> items and project them to something else, such as a property of the type, or a new type. It makes heavy use of generic type inference from the compiler to do this without <> everywhere.
In your example, the IEnumerable<T> is the string[] of lines from the file. The Select func creates an anonymous type (also making use of generic type inference) and assigns some properties based on splitting each line l, which is a string from your enumerable.
OrderBy is another IEnumerable<T> extension method and proceeds to return an IEnumerable<T> in the order based on the expression you provide.
T at this point is the anonymous type from the Select with two properties (myIdentiafication and myName), so the OrderBy(i => i.Id) bit won't compile. It can be fixed:
.OrderBy(i => i.myIdentiafication);
This is a LINQ query. Enumerable.Select projects each line from file into anonymous object with properties myIdentiafication and myName. Then you sort sequence of anonymous objects with Enumerable.OrderBy. But you should select property which exists in anonymous object. E.g. myIdentiafication because there is no id property:
var lines = File.ReadLines(#"c:\temp\mycsvfil3.csv") // get sequence of lines
.Select(l => new {
myIdentiafication = int.Parse(l.Split(',')[0].Trim()),
myName= l.Split(',')[1].Trim()
}).OrderBy(i => i.myIdentiafication);
NOTE: To avoid parsing each line twice, you can use query syntax with introducing new range variables:
var lines = from l in File.ReadLines(#"c:\temp\mycsvfil3.csv")
let pair = l.Split(',')
let id = Int32.Parse(pair[0].Trim())
orderby id
select new {
Id = id,
Name = pair[1].Trim()
};
From each string returned by ReadLines create an anonymous object with two properties (myIdentiaficiation and myName). Within the Select the context variable l represents a single line from the set returned by ReadLines.
In C#, I have an object type 'A' that contains a list of key value pairs.
The key value pairs is a category string and a value string.
To instantiate object type A, I would have to do the following:
List<KeyValuePair> keyValuePairs = new List<KeyValuePair>();
keyValuePairs.Add(new KeyValuePair<"Country", "U.S.A">());
keyValuePairs.Add(new KeyValuePair<"Name", "Mo">());
keyValuePairs.Add(new KeyValuePair<"Age", "33">());
A a = new A(keyValuePairs);
Eventually, I will have a List of A object types and I want to manipulate the list so that i only get unique values and I base it only on the country name. Therefore, I want the list to be reduced to only have ONE "Country", "U.S.A", even if it appears more than once.
I was looking into the linq Distinct, but it does not do what I want because it I can't define any parameters and because it doesn't seem to be able to catch two equivalent objects of type A. I know that I can override the "Equals" method, but it still doesn't solve the my problem, which is to render the list distinct based on ONE of the key value pairs.
To expand upon Karl Anderson's suggestion of using morelinq, if you're unable to (or don't want to) link to another dll for your project, I implemented this myself awhile ago:
public static IEnumerable<T> DistinctBy<T, U>(this IEnumerable<T> source, Func<T, U>selector)
{
var contained = new Dictionary<U, bool>();
foreach (var elem in source)
{
U selected = selector(elem);
bool has;
if (!contained.TryGetValue(selected, out has))
{
contained[selected] = true;
yield return elem;
}
}
}
Used as follows:
collection.DistinctBy(elem => elem.Property);
In versions of .NET that support it, you can use a HashSet<T> instead of a Dictionary<T, Bool>, since we don't really care what the value is so much as that it has already been hashed.
Check out the DistinctBy syntax in the morelinq project.
A a = new A(keyValuePairs);
a = a.DistinctBy(k => new { k.Key, k.Value }).ToList();
You need to select the distinct property first:
Because it's a list inside a list, you can use the SelectMany. The SelectMany will concat the results of subselections.
List<A> listOfA = new List<A>();
listOfA.SelectMany(a => a.KeyValuePairs
.Where(keyValue => keyValue.Key == "Country")
.Select(keyValue => keyValue.Value))
.Distinct();
This should be it. It will select all values where the key is "Country" and concat the lists. Final it will distinct the country's. Given that the property KeyValuePairs of the class A is at least a IEnumerable< KeyValuePair< string, string>>
var result = keyValuePairs.GroupBy(x => x.Key)
.SelectMany(g => g.Key == "Country" ? g.Distinct() : g);
You can use the groupby statement. From here you can do all kind off cool stuf
listOfA.GroupBy(i=>i.Value)
You can groupby the value and then sum all the keys or something other usefull
I have a List of custom objects:
List<SomeObject> someobjects = getAllSomeObjects();
List<SomeObject> someobjectsfiltered = new List<SomeObject>();
class SomeObject
{
List <AnotherList>
}
class AnotherList
{
public string Name{get;set;}
public Categories category {get;set;}
}
So I'm trying to get All AnotherList items of a specific type using Lambda
someobjectsfiltered = someobjects.SelectMany(s => s.AnotherList.FindAll(a => a.category == SomeCategory));
But I get the
Can not implicitly convert type
IEnumerable to Generic.List
error
Any idea how to solve this?
Many thanks.
You need to throw a ToList() on the end or change the type of the result to IEnumerable<SomeObject> because, as the error says, you can't assign an IEnumerable<T> to a variable of type List<T>.
someobjectsfiltered = someobjects.SelectMany(s => s.AnotherList.FindAll(a => a.category == SomeCategory))
.ToList();
Edit based on comments
If what you want is the SomeObjects that have a list containing an item that matches the category you can do that using.
someobjectsfiltered = someobjects.Where( s => s.AnotherList.Any( a => a.category == SomeCategory ))
.ToList();
#tvanfosson #Maya Not sure how "Any" would work here since it will come back with true all the time causing the whole AnotherList (the inner list) to be selected back, if that "Category" type exist once in the inner list then all of its contents will be selected including those with the unwanted Category types.
SelectMany returns IEnumerable<T> - you can convert that to a List<T> by simply adding a call to ToList() onto the end.
someobjectsfiltered = someobjects.SelectMany(s => s.AnotherList.FindAll(a => a.category == SomeCategory)).ToList();
I have a field object and I create a list of fields:
class Field {
string objectName;
string objectType;
string fieldName;
string fieldValue;
//constructor...
}
List<Field> fieldList = new List<Field>();
Suppose I wanted to query this list to return a collection of distinct object names (to then be inserted into a checkedlistbox. How would I go about doing that?
I imagine some LINQ magic can manage this?
The expression should return a List of distinct object names from the list as defined. I converted it to a list since the docs for the CheckedListBox DataSource property indicated that it needs to implement IList or IListSource, not merely IEnumerable.
((ListControl)cbListBox).DataSource = fieldList.Select( f => f.objectName )
.Distinct()
.ToList() );
If accessing the checkedListBox as a ListControl doesn't give access to the DataSource (sometimes the docs lie), you could try:
cbListBox.Items.AddRange( fieldList.Select( f => f.objectName )
.Distinct()
.ToArray() );
Either of these work
Using var
1) var fieldNameCollection = from f in fieldList select f.FieldName;
2) Lambda syntax
var fieldNameCollection = fieldList.Select(f => f.FieldName);
Alternately, instead of using var, you can also use
IEnumerable fieldNameCollection = fieldList.Select(f => f.FieldName);
var q = from Field f in fileldList select f.objectName;
chkBoxList.DataSource = q.Distinct();