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
Related
I have a List every item of that list is user defined class which has a property which type is string but its content is datetime, like the following;
StringAsDate="23/10/2020 17:12:00"
class Unknown{
...,
StringAsDate,
...
}
list of T list
I want to sort the list descending which is string that container DateTime, i have to preserve the format of list at the end.
I can do that by brute force but im looking for efficient and elegant way.
Thanks.
Based on your comments on the answers you are looking for a reflection based solution.
The reflection part, to get the value of a property based on the name:
x.GetType().GetProperty(propertyName).GetValue(x, null)
Try this:
var propetyName = "StringAsDate";
var listSorted = list.OrderByDescending(x => DateTime.Parse(Convert.ToString(x.GetType().GetProperty(propertyName).GetValue(x, null))));
If the Property is already of type DateTime a cast would be sufficient
var propetyName = "StringAsDate";
var listSorted = list.OrderByDescending(x => (DateTime)x.GetType().GetProperty(propertyName).GetValue(x, null));
List<Uknown> list = example for your list containing the objects.
List<Uknown> sorted = list.OrderByDescending(x => DateTimeParse(x.StringAsDate));
Using LINQ:
List<UnKnown> listSorted = list.OrderByDescending(x=>DateTime.Parse(x.StringAsDate));
You can create a list of object.
List<object> sorted = list.OrderByDescending(x => DateTime.Parse(d.GetType().GetField("StringAsDate").GetValue(d).ToString())));
With this your code will work with any class as long as they have this field.
But in case you have control on the Classes being sent to your collection I would suggest that you implement an interface on all the Classes ITimeStamp
and then
List<ITimeStamp> sorted = list.OrderByDescending(x => DateTime.Parse(x.StringAsDate));
I have a foreach loop that goes through a panel of controls:
foreach(MyControl control in MyPanel.Controls)
and adds the name of each checkbox control that's checked to a list:
if (control.Checked == true)
{
MyList.Add(control.Name)
}
but it seems to jump all around the panel in the order it adds them to the list. How can I set the order it runs through the panel so that it adds the names of the controls that are checked to MyList in a specified order?
Cast the collection to an IEnumerable<Control> and use OrderBy:
foreach(MyControl control in MyPanel.Controls.Cast<Control>.OrderBy(c => c.Name))
{
if (control.Checked == true)
{
MyList.Add(control.Name)
}
}
Or sort the list after adding all of the names, since you're only storing the name in the list:
MyList.Sort();
You can use Linq to Create your list
var MyList = MyPanel.Controls.Cast<Control>.OrderBy(c => c.Name).Where(c=>c.Checked).ToList();
Try this:
var MyList = MyPanel.Controls.OfType<CheckBox>()
.Where(c => c.Checked)
.OrderBy(c => c.Name)
.ToList();
As you might see, I'm using OfType instead of Cast because Cast will throw if it can't cast to CheckBox, whereas OfType will only return objects that CAN be cast to the desired type.
Cheers
MyPanel.Controls return a ControlCollection. A ControlCollection implements the IList interface, so you can use LINQ to sort it.
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()
I am currently ordering a list of custom objects using the IQueryable OrderBy method as follows:
mylist.AsQueryable().OrderBy("PropertyName");
Now I am looking to sort by more than one property. Is there any way to do that?
Thanks,
Yannis
OrderBy(i => i.PropertyName).ThenBy(i => i.AnotherProperty)
In OrderBy and ThenBy you have to provide keySelector function, which chooses key for sorting from object. So if you know property name only at runtime then you can make such function with Reflection like:
var propertyInfo = i.GetType().GetProperty("PropertyName");
var sortedList = myList.OrderBy(i => propertyInfo.GetValue(i, null))
But it will be slower, then direct access to property. Also you can "compile" such function on the fly with Linq.Expressions and it will work faster than reflection but it is not very easy. Or you can use CollectionViewSource and their sorting ablilities in WPF.
And don't forget that OrderBy() returns sorted enumerable and it does not sort your existed List inplace. In your example you did not save sorted list to variable.
You could use .ThenBy:
var result = mylist
.AsQueryable()
.OrderBy(x => x.PropertyName)
.ThenBy(x => x.SomeOtherProperty);
You probably want to use the ThenBy extension method to be able to sort by multiple fields
return myList.AsQueryable().OrderBy(m=>m.Property1).ThenBy(m => m.Property2);
If you want dynamic Linq, look at LinqKit. I recently implemented Microsoft's dynamic Linq library from here and was able to sort by two fields using a string.
Awesome stuff! Not sure if this will be in .NET 5 or not.
As others have suggested, you can use 'ThenBy'. If you want to convert a string to a different value before using it, this is possible too, for example...
var sortedSystemTestResultsList = systemTestResultsList.OrderBy(s =>
{
DateTime dt;
if (!DateTime.TryParse(s.testPointCompletedDate, out dt)) return DateTime.MaxValue;
return dt;
}).ThenBy(s =>
{
Int32 tpID;
if (!Int32.TryParse(s.testRunResultID, out tpID)) return Int32.MaxValue;
return tpID;
});
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();