I would bind the values of an enumeration with a combobox control.
I've written this code:
cboPriorLogicalOperator.DataSource = Enum.GetValues(typeof(MyEnum))
.Cast<MyEnum>()
.Select(p => new { Key = (int)p, Value = p.ToString() })
.ToList();
myComboBox.DisplayMember = "Value";
myComboBox.ValueMember = "Key";
It works well but I wonder if there is a simpler way.
I think your code is beautiful!
The only improvement would be to place the code in an Extension Method.
EDIT:
When I think about it, what you want to do is to use the Enum as in the definition and not an instance of the enum, which is required by extensions methods.
I found this question, which solves it really nicely:
public class SelectList
{
// Normal SelectList properties/methods go here
public static SelectList Of<T>()
{
Type t = typeof(T);
if (t.IsEnum)
{
var values = from Enum e in Enum.GetValues(t)
select new { ID = e, Name = e.ToString() };
return new SelectList(values, "Id", "Name");
}
return null;
}
}
// called with
var list = SelectList.Of<Things>();
Only you might want to return a Dictionary<int, string> and not a SelectList, but you get the idea.
EDIT2:
Here we go with a code example that covers the case you are looking at.
public class EnumList
{
public static IEnumerable<KeyValuePair<T, string>> Of<T>()
{
return Enum.GetValues(typeof (T))
.Cast<T>()
.Select(p => new KeyValuePair<T, string>(p, p.ToString()))
.ToList();
}
}
Or this version perhaps, where the key is an int
public class EnumList
{
public static IEnumerable<KeyValuePair<int, string>> Of<T>()
{
return Enum.GetValues(typeof (T))
.Cast<T>()
.Select(p => new KeyValuePair<int, string>(Convert.ToInt32(p), p.ToString()))
.ToList();
}
}
Why not to use:
myComboBox.DataSource = Enum.GetValues(typeof(MyEnum))
?
foreach (int r in Enum.GetValues(typeof(MyEnum)))
{
var item = new ListItem(Enum.GetName(typeof(MyEnum), r), r.ToString());
ddl.Items.Add(item);
}
I recently ran into a problem where I had a nullable enum property and needed to bind it to a ComboBox. Here is the solution I came up with:
using System;
using System.Collections.Generic;
namespace ActivitySchedule.Model
{
public class NullableEnum<T> where T : struct, IComparable
{
public string Display { get; private set; }
public T? Value { get; private set; }
public static implicit operator T?(NullableEnum<T> o)
{
return o.Value;
}
public static implicit operator NullableEnum<T>(T? o)
{
return new NullableEnum<T>
{
Display = o?.ToString() ?? "NA",
Value = o
};
}
private NullableEnum() { }
public static IEnumerable<NullableEnum<T>> GetList()
{
var items = new List<NullableEnum<T>>
{
new NullableEnum<T>
{
Display = "NA",
Value = null
}
};
var values = Enum.GetValues(typeof(T));
foreach (T v in values)
{
items.Add(v);
}
return items;
}
}
}
I wrapped the object in a Controller class and change the property's Type like so:
private MyClass myClass;
public NullableEnum<MyEnum> MyEnum
{
get { return this.myClass.MyEnum; }
set { this.myClass.MyEnum = value.Value; }
}
(it could also be a derived class and override the property)
This is how I used it:
var types = NullableEnum<MyEnum>.GetList();
this.comboBox1.DataSource = types;
this.comboBox1.DisplayMember = "Display";
this.comboBox1.ValueMember = "Value";
this.comboBox1.Bindings.Add("SelectedValue", myClassController, "MyEnum");
private void Form1_Load(object sender, EventArgs e)
{
comboBox1.DataSource = Enum.GetValues( typeof(Gender));
Array gen = Enum.GetValues(typeof(Gender));
List<KeyValuePair<string, char>> lstgender = new List<KeyValuePair<string,char>>();
foreach(Gender g in gen)
lstgender.Add(new KeyValuePair<string,char>(g.ToString(),((char)g)));
comboBox1.DataSource = lstgender;
comboBox1.DisplayMember = "key";
comboBox1.ValueMember = "value"
}
private void button1_Click(object sender, EventArgs e)
{
MessageBox.Show(comboBox1.SelectedValue.ToString());
}
public class Student
{
public string stud_name { get; set; }
public Gender stud_gen { get; set; }
}
public enum Gender
{
Male='M',
Female='F'
}
Related
I have two lists AuthorList & AuthorList2. At the moment I am using union with simple IEqualityComparer class.
I expect to have a result list without any duplicates from AuthorList & AuthorList2 and if there are any duplicates in those lists, they need to be removed from the lists and the Author class Assigned property needs to be set true for the duplicate item.
Existing information from both AuthorLists:
ProductID & Assigned
1, false
2, false
3, false
1, false
Result list:
ProductID & Assigned
1, true
2, false
3, false
The logic needs to filter out duplicates and if both of those lists have the same element, change false -> true.
namespace HelloWorld
{
class Hello
{
static void Main()
{
List<Author> AuthorList = new List<Author>
{
new Author(1, false),
new Author(2, false),
new Author(3, false)
};
List<Author> AuthorList2 = new List<Author>
{
new Author(1, false)
};
var compareById = new AuthorComparer(false);
var result = AuthorList.Union(AuthorList2, compareById);
foreach (var item in result)
{
Console.WriteLine("Result: {0},{1}", item.ProductId, item.Assigned);
}
Console.ReadKey();
}
public class AuthorComparer : IEqualityComparer<Author>
{
private bool m_withValue;
public AuthorComparer(bool withValue)
{
m_withValue = withValue;
}
public bool Equals(Author x, Author y)
{
return (x.ProductId == y.ProductId);
}
public int GetHashCode(Author x)
{
return x.ProductId.GetHashCode();
}
}
public class Author
{
private int productId;
private bool assigned;
public Author(int productId, bool assigned)
{
this.productId = productId;
this.assigned = assigned;
}
public int ProductId
{
get { return productId; }
set { productId = value; }
}
public bool Assigned
{
get { return assigned; }
set { assigned = value; }
}
}
}
}
The code you're looking for is this:
AuthorList.ForEach(a => a.Assigned = AuthorList2.Exists(b => b.ProductId == a.ProductId));
You don't need the IEqualityComparer at all.
Full, working code:
namespace HelloWorld
{
class Hello
{
static void Main()
{
List<Author> AuthorList = new List<Author>
{
new Author(1, false),
new Author(2, false),
new Author(3, false)
};
List<Author> AuthorList2 = new List<Author>
{
new Author(1, false)
};
AuthorList.ForEach(a => a.Assigned = AuthorList2.Exists(b => b.ProductId == a.ProductId));
foreach (var item in AuthorList)
{
Console.WriteLine("Result: {0},{1}", item.ProductId, item.Assigned);
}
Console.ReadKey();
}
public class Author
{
public Author(int productId, bool assigned)
{
this.ProductId = productId;
this.Assigned = assigned;
}
public int ProductId { get; set; }
public bool Assigned { get; set; }
}
}
}
With your comparer, you can use:
foreach (var author in AuthorList.Intersect(AuthorList2, compareById))
{
author.Assigned = true;
}
You can do it without that easily enough as well if you don't need the comparer for anything else:
var author2Ids = new HashSet<int>(AuthorList2.Select(a => a.ProductId));
foreach (var author in AuthorList.Where(a => author2Ids.Contains(a.ProductId)))
{
author.Assigned = true;
}
... but if you need the comparer for anything else, or if it may get more complex, I'd stick to using that.
Try with something like this. Please look at ! carefully
//Separate out different entries from both the lists
var diffList = AuthorList.Where(x => !AuthorList2.Any(y => y.ProductId== x.ProductId && y.Assigned== x.Assigned)).ToList();
//Separate out common entries from both the list
var commonList = AuthorList.Where(x => AuthorList2.Any(y => y.ProductId== x.ProductId && y.Assigned== x.Assigned)).ToList();
//Change value of Assigned
commonList.ForEach(x => x.Assigned = !x.Assigned);
//Merge both the lists
diffList.AddRange(commonList);
POC: DotNetFiddler
This will produce a new List of elements without altering the source:
private static List<Author> FilterDuplicates(List<Author> x, List<Author> y)
{
return x.Select(author => new Author(author.ProductId, y.Exists(a => a.ProductId == author.ProductId))).ToList();
}
I have the following code.
public class SyncProperty : Attribute
{
public readonly string PropertyName;
public SyncProperty(string propertyName)
{
this.PropertyName = propertyName;
}
}
public class SyncContact
{
[SyncProperty("first_name")]
public string FirstName { get; set; }
[SyncProperty("last_name")]
public string LastName { get; set; }
[SyncProperty("phone")]
public string Phone { get; set; }
[SyncProperty("email")]
public string Email { get; set; }
}
I need to create an instance of my SyncContact such as
var contact = new SyncContact { FirstName="Test", LastName="Person", Phone="123-123-1234", Email="test#test.com"};
And then with that object I need to create a NameValueCollection where the object's property uses the SyncProperty's PropertyName as the Key in the collection. Then use that to make a post request to an API. So in this case I would end up with a collection like...
collection["first_name"] = "Test"
collection["last_name"] = "Person"
collection["phone"] = "123-123-1234"
collection["email"] = "test#test.com"
How can I do that?
If you want to get some type metadata, you should use Reflection. To read attribute you can use GetCustomAttribute<AttributeType>() extension for MemberInfo.
This extension method builds sync dictionary from type properties decorated with SyncPropertyAttribute (I suggest to use the dictionary instead of NameValueCollection):
public static Dictionary<string, string> ToSyncDictionary<T>(this T value)
{
var syncProperties = from p in typeof(T).GetProperties()
let name = p.GetCustomAttribute<SyncProperty>()?.PropertyName
where name != null
select new {
Name = name,
Value = p.GetValue(value)?.ToString()
};
return syncProperties.ToDictionary(p => p.Name, p => p.Value);
}
Usage:
var collection = contact.ToSyncDictionary();
Output:
{
"first_name": "Test",
"last_name": "Person",
"phone": "123-123-1234",
"email": "test#test.com"
}
Note: if you are going to use contact data in POST request, then you should consider using simple JSON serialization attributes instead of creating your own attributes. E.g. with Json.NET:
public class SyncContact
{
[JsonProperty("first_name")]
public string FirstName { get; set; }
[JsonProperty("last_name")]
public string LastName { get; set; }
[JsonProperty("phone")]
public string Phone { get; set; }
[JsonProperty("email")]
public string Email { get; set; }
}
Then simple serialization will do the job:
string json = JsonConvert.SerializeObject(contact);
And the result will be exactly same as above.
The attributes belong to the properties of the class, so you need to get the type of the class, then find the appropriate properties and then get the custom attributes.
Something like:
var contact = new SyncContact { FirstName="Test", LastName="Person", Phone="123-123-1234", Email="test#test.com"};
var t = typeof(SyncContact);
var props = t.GetProperties().Select(p => new { p, attr = p.GetCustomAttribute<SyncProperty>() }).Where(p => p.attr != null);
var dict = props.ToDictionary(p => p.attr.PropertyName, v => v.p.GetValue(contact) );
This leaves out any properties that aren't tagged, but you can decide to handle those differently if you wanted to.
Fiddle
Assuming SyncProperty is tagged on every property, this should do the job:
var contact = new SyncContact { FirstName="Test", LastName="Person", Phone="123-123-1234", Email="test#test.com"};
var collection = contact.GetType().GetProperties()
.Select(x => new
{
x.GetCustomAttribute<SyncProperty>().PropertyName,
Value = x.GetValue(contact).ToString()
})
.ToDictionary(x => x.PropertyName, x => x.Value);
As a helper method:
public static class SynxHelper
{
public static Dictionary<string, string> Serialize<T>(T obj)
{
return typeof(T).GetProperties()
.Select(x => new
{
SyncProperty = x.GetCustomAttribute<SyncProperty>(),
Value = x.GetValue(obj)
})
.Where(x => x.SyncProperty != null)
.ToDictionary(x => x.SyncProperty.PropertyName, x => x.Value.ToString());
}
}
// usage
var collection = SynxHelper.Serialize(contact);
Here is a one line linq solution for this
(from prop in obj.GetType().GetProperties()
where prop.GetCustomAttribute<SyncProperty>() != null
select new { Key = prop.GetCustomAttribute<SyncProperty>().PropertyName, Value = prop.GetValue(obj) })
.ToDictionary(k => k.Key, v => v.Value);
BUT!!!!!!!! Don't try doing this your self. This is not optimized and slow as all reflection is.
This is just to demonstrate how bad reflection is
static void Main(string[] args)
{
var data = Enumerable.Range(0, 10000).Select(i => new SyncContact { FirstName = "Test", LastName = "Person", Phone = "123-123-1234", Email = "test#test.com" }).ToArray();
Stopwatch sw = new Stopwatch();
long m1Time = 0;
var total1 = 0;
sw.Start();
foreach (var item in data)
{
var a = ToSyncDictionary(item);
total1++;
}
sw.Stop();
m1Time = sw.ElapsedMilliseconds;
sw.Reset();
long m2Time = 0;
var total2 = 0;
sw.Start();
foreach (var item in data)
{
var a = ToSyncDictionary2(item);
total2++;
}
sw.Stop();
m2Time = sw.ElapsedMilliseconds;
Console.WriteLine($"ToSyncDictionary : {m1Time} for {total1}");
Console.WriteLine($"ToSyncDictionary2 : {m2Time} for {total2}");
Console.ReadLine();
}
public static IDictionary<string, string> ToSyncDictionary<T>(T value)
{
var syncProperties = from p in typeof(T).GetProperties()
let name = p.GetCustomAttribute<SyncProperty>()?.PropertyName
where name != null
select new
{
Name = name,
Value = p.GetValue(value)?.ToString()
};
return syncProperties.ToDictionary(p => p.Name, p => p.Value);
}
public static IDictionary<string, string> ToSyncDictionary2<T>(T value)
{
return Mapper<T>.ToSyncDictionary(value);
}
public static class Mapper<T>
{
private static readonly Func<T, IDictionary<string, string>> map;
static Mapper()
{
map = ObjectSerializer();
}
public static IDictionary<string, string> ToSyncDictionary(T value)
{
return map(value);
}
private static Func<T, IDictionary<string, string>> ObjectSerializer()
{
var type = typeof(Dictionary<string, string>);
var param = Expression.Parameter(typeof(T));
var newExp = Expression.New(type);
var addMethod = type.GetMethod(nameof(Dictionary<string, string>.Add), new Type[] { typeof(string), typeof(string) });
var toString = typeof(T).GetMethod(nameof(object.ToString));
var setData = from p in typeof(T).GetProperties()
let name = p.GetCustomAttribute<SyncProperty>()?.PropertyName
where name != null
select Expression.ElementInit(addMethod,
Expression.Constant(name),
Expression.Condition(Expression.Equal(Expression.Property(param, p), Expression.Constant(null)),
Expression.Call(Expression.Property(param, p), toString),
Expression.Constant(null,typeof(string))));
return Expression.Lambda<Func<T, IDictionary<string, string>>>(Expression.ListInit(newExp, setData), param).Compile();
}
}
On my machine I got a 10x boost in pers.
If you can use some serializer like JSON.net since you will need to change a lot of things to make it work well and you already have a tone of stuff that dose it for you.
I have found some direction for this problem but have not found anything which I can apply to this problem.
I want to filter lists of different types by stated properties they hold. I can use linq to dynamically filter a List by Test.id but I cant manage to filter a List through MyClass.Name
I have these classes.
public class Test
{
public int Id { get; set; }
public MyClass myclass { get; set; }
}
public class MyClass
{
public string Name { get; set; }
}
This is what I'm trying to do.
static void Main(string[] args)
{
var source = new List<Test> {
new Test { Id = 1,myclass = new MyClass() { Name = "bob" } },
new Test { Id = 2,myclass= new MyClass() { Name = "joe" } } };
var x = myFilter(source,"Name", "bob");
Console.WriteLine(x.Count());
}
public static IEnumerable<T> myFilter<T>(List<T> source, string propertyName, string searchString)
{
// get the myclass property then the stated property(Name) value within it
searchString = searchString.ToLower();
return source.Where(s => (s.GetType().GetProperty("myclass")
.GetType().GetProperty(propertyName)
.GetValue(s.GetType().GetProperty("myclass"),null).ToString() ?? " ")
.ToLower().Contains(searchString));
}
The count return 0 when I am expecting 1. for Test.MyClass.Name = "bob"
Is there a solution for this or is there a better way to do it besides reflection?
Thanks
you need to use the PropertyType of the returned myclass property:
public static IEnumerable<T> myFilter<T>(List<T> source, string propertyName, string searchString)
{
// get the myclass property then the stated property(Name) value within it
searchString = searchString.ToLower();
return source.Where(s => (s.GetType().GetProperty("myclass")
.PropertyType.GetProperty(propertyName)
.GetValue(s.GetType().GetProperty("myclass").GetValue(s)).ToString() ?? " ")
.ToLower().Contains(searchString));
}
You should be able to use the following:
var count = source.Count(test =>
string.Compare(test.myClass.Name, "Bob",
StringComparison.CurrentCultureIgnoreCase) == 0);
This will compare the string value of the Name Property and only count where the name is equal to "bob" and it will ignore the case.
If you want to return the Test object instead then you can use the following
var results = source.Where(test =>
string.Compare(test.myClass.Name, "Bob",
StringComparison.CurrentCultureIgnoreCase) == 0);
I am trying to compare all possible values in a list of objects like this:
public class Object21
{
int Id,
bool firstbool,
bool secondbool
}
I would loop through the objects and compare them like this:
List<Object1> objects;
foreach(var o in objects)
{
if(firstbool && secondbool)
....
if(firstbool && !secondbool)
....
if(!firstbool && secondbool)
....
if(!firstbool && !secondbool)
....
}
This seems ok, but what if the object had several values that you were running through if statements.
public class Object2
{
int Id;
int firstbool;
....
int twentiethbool;
}
Then you would have to write out all of the possible conditional statements and your code would be terribly written and hard to read.
List<Object2> objects2;
foreach(var o in objects2)
{
if(firstbool && secondbool && ... && twentiethbool)
....
if(....)
....
....
....
if(!firstbool && !secondbool && ... && !twentiethbool)
....
}
Is there a simpler way to write the second scenario so that you are not writing every combination of if statements?
In the end I would like to calculate the percentage occurrence of each condition in the list.
To answer the first part of the question (about comparing every combination):
There isn't really a good way to do that, other than write a bunch of if statements. Of course; you probably shouldn't be doing that anyways :)
You could probably use reflection and recursion, but thats going to get messy really fast.
Luckily, to just get the percentage occurrence of each flag, you can just do:
list.Count(i => i.firstbool) / (double)list.Count();
...
first, create a dictionary to save all conditions
var dict = new Dictionary<string, int>{{"001",0},{"010",0} ...}
then, create key use bool values
var key=string.Empty;
key+=firstbool ?"0":"1"
key+=secondbool ?"0":"1"
......
after all, you can know which condition occurred
dict[key]++;
Given a class structure like this:
public class YourClass
{
public int Id { get; set; }
public bool firstbool { get; set; }
public bool secondbool { get; set; }
public bool thirdbool { get; set; }
}
You can use reflection to get all the boolean values (and only bool values) inside the class:
public IEnumerable<bool> GetBools(YourClass obj)
{
return obj.GetType()
.GetProperties(BindingFlags.Public | BindingFlags.Instance)
.Where(x => x.PropertyType == typeof (bool))
.Select(x => (bool)x.GetValue(obj, null));
}
Then use LINQ to iterate through the collection, and create a dictionary of combinations and totals:
List<YourClass> objects = new List<YourClass>();
var totals = objects.GroupBy(x => String.Join(",", GetBools(x)))
.ToDictionary(x => x.Key, x => x.Count() / (double)objects.Count);
This will give you a dictionary with each unique combination and the percentage it occurs.
Given this input:
var o = new List<YourClass>
{
new YourClass {firstbool = true, secondbool = true, thirdbool = false},
new YourClass {firstbool = false, secondbool = false, thirdbool = false},
new YourClass {firstbool = true, secondbool = true, thirdbool = false}
};
The result in the dictionary will be:
{["True,True,False", 0.666666666666667]}
{["False,False,False", 0.333333333333333]}
it's probably easier to rewrite your class, storing each condition in an array like follows:
public class MyObject
{
public static int numFields = 20;
public enum Conditions
{
C1, C2, C3, .... C20 //name for each condition, so can set values using descriptive names
};
public Boolean[] BinaryFields = new Boolean[numFields];
public void setCondition(Conditions condition, Boolean value)
{
BinaryFields[(int)condition] = value;
}
public override string ToString()
{
return string.Join(",", BinaryFields);
}
}
then you can calculate the stat by counting what is actually there, instead of numerating through all of the 2^20 possibilities. something like follows:
static void Main(string[] args)
{
//simulation: creat 10 MyObjects
List<MyObject> lst = new List<MyObject>();
for (int i = 0; i < 10; i++)
{
MyObject m = new MyObject();
//example of setting condition
m.setCondition(MyObject.Conditions.C1, true);
lst.Add(m);
}
//calculate stat
var resultCount = new Dictionary<string, int>(); //conditionResult, count
foreach (MyObject m in lst)
{
if (resultCount.ContainsKey(m.ToString()))
{
resultCount[m.ToString()] += 1;
}
else
{
resultCount.Add(m.ToString(), 1);
}
}
//print stat
foreach(KeyValuePair<string, int> entry in resultCount){
Debug.WriteLine("probability for conditoin={0} is {1}", entry.Key, (double)entry.Value / lst.Count);
}
}
If you have some unique action for each boolean properties combination I suggest you to use some kind of string key for your object, generated on those values. Something like "001001", "000000" etc. Then use Dictionary<string, Func<int>> to hold all your unique actions, get and perform the right one by it's key. For example:
public class Object21
{
public int Id { get; set; }
public bool FirstBool { get; set; }
public bool SecondBool { get; set; }
public bool ThirdBool { get; set; }
public bool FourthBool { get; set; }
public bool FifthBool { get; set; }
public bool SixthBool { get; set; }
public void Process()
{
// Perform the action
Actions[Key]();
}
// Returns "001001" like representation of your object
public string Key
{
get
{
return string.Join(string.Empty, GetType()
.GetProperties(BindingFlags.Public | BindingFlags.Instance)
.Where(x => x.PropertyType == typeof(bool))
.Select(x => (bool)x.GetValue(this, null) ? "1" : "0" ));
}
}
private static Dictionary<string, Func<int>> Actions
{
get
{
return new Dictionary<string, Func<int>>
{
{"000000", new Func<int>(delegate
{
Console.WriteLine("000000 action performed.");
return 0;
})},
{"000001", new Func<int>(delegate
{
Console.WriteLine("000001 action performed.");
return 1;
})},
{"000010", new Func<int>(delegate
{
Console.WriteLine("000010 action performed.");
return 2;
})},
// More actions
{"111111", new Func<int>(delegate
{
Console.WriteLine("111111 action performed.");
return 63;
})}
};
}
}
}
And then use this in your program like this:
static void Main(string[] args)
{
var list = new List<Object21>
{
// initialize your list
};
foreach (var object21 in list)
{
object21.Process();
}
// Calculate your occurrences (basically what #Grant Winney suggested)
var occurrences = list.GroupBy(o => o.Key).ToDictionary(g => g.Key, g => (g.Count() / (double)list.Count)*100);
foreach (var occurrence in occurrences)
{
Console.WriteLine("{0}: {1}%", occurrence.Key, occurrence.Value);
}
}
I need a basic example of how to use the IComparable interface so that I can sort in ascending or descending order and by different fields of the object type I'm sorting.
Well, since you are using List<T> it would be a lot simpler to just use a Comparison<T>, for example:
List<Foo> data = ...
// sort by name descending
data.Sort((x,y) => -x.Name.CompareTo(y.Name));
Of course, with LINQ you could just use:
var ordered = data.OrderByDescending(x=>x.Name);
But you can re-introduce this in List<T> (for in-place re-ordering) quite easily; Here's an example that allows Sort on List<T> with lambda syntax:
using System;
using System.Collections.Generic;
class Foo { // formatted for vertical space
public string Bar{get;set;}
}
static class Program {
static void Main() {
List<Foo> data = new List<Foo> {
new Foo {Bar = "abc"}, new Foo {Bar = "jkl"},
new Foo {Bar = "def"}, new Foo {Bar = "ghi"}
};
data.SortDescending(x => x.Bar);
foreach (var row in data) {
Console.WriteLine(row.Bar);
}
}
static void Sort<TSource, TValue>(this List<TSource> source,
Func<TSource, TValue> selector) {
var comparer = Comparer<TValue>.Default;
source.Sort((x,y)=>comparer.Compare(selector(x),selector(y)));
}
static void SortDescending<TSource, TValue>(this List<TSource> source,
Func<TSource, TValue> selector) {
var comparer = Comparer<TValue>.Default;
source.Sort((x,y)=>comparer.Compare(selector(y),selector(x)));
}
}
Here's a simple example:
public class SortableItem : IComparable<SortableItem>
{
public int someNumber;
#region IComparable<SortableItem> Members
public int CompareTo(SortableItem other)
{
int ret = -1;
if (someNumber < other.someNumber)
ret = -1;
else if (someNumber > other.someNumber)
ret = 1;
else if (someNumber == other.someNumber)
ret = 0;
return ret;
}
#endregion
}
"That's great, but what if I want to be able to control the sort order, or sort by another field?"
Simple. All we need to do is add few more fields to the object. First we'll add a string for a different sort type and then we'll add a boolean to denote whether we're sorting in descending or ascending order and then add a field which determines which field we want to search by.
public class SortableItem : IComparable<SortableItem>
{
public enum SortFieldType { SortNumber, SortString }
public int someNumber = -1;
public string someString = "";
public bool descending = true;
public SortFieldType sortField = SortableItem.SortFieldType.SortNumber;
#region IComparable<SortableItem> Members
public int CompareTo(SortableItem other)
{
int ret = -1;
if(sortField == SortableItem.SortFieldType.SortString)
{
// A lot of other objects implement IComparable as well.
// Take advantage of this.
ret = someString.CompareTo(other.someString);
}
else
{
if (someNumber < other.someNumber)
ret = -1;
else if (someNumber > other.someNumber)
ret = 1;
else if (someNumber == other.someNumber)
ret = 0;
}
// A quick way to switch sort order:
// -1 becomes 1, 1 becomes -1, 0 stays the same.
if(!descending) ret = ret * -1;
return ret;
}
#endregion
public override string ToString()
{
if(sortField == SortableItem.SortFieldType.SortString)
return someString;
else
return someNumber.ToString();
}
}
"Show me how!"
Well since you asked so nicely.
static class Program
{
static void Main()
{
List<SortableItem> items = new List<SortableItem>();
SortableItem temp = new SortableItem();
temp.someString = "Hello";
temp.someNumber = 1;
items.Add(temp);
temp = new SortableItem();
temp.someString = "World";
temp.someNumber = 2;
items.Add(temp);
SortByString(items);
Output(items);
SortAscending(items);
Output(items);
SortByNumber(items);
Output(items);
SortDescending(items);
Output(items);
Console.ReadKey();
}
public static void SortDescending(List<SortableItem> items)
{
foreach (SortableItem item in items)
item.descending = true;
}
public static void SortAscending(List<SortableItem> items)
{
foreach (SortableItem item in items)
item.descending = false;
}
public static void SortByNumber(List<SortableItem> items)
{
foreach (SortableItem item in items)
item.sortField = SortableItem.SortFieldType.SortNumber;
}
public static void SortByString(List<SortableItem> items)
{
foreach (SortableItem item in items)
item.sortField = SortableItem.SortFieldType.SortString;
}
public static void Output(List<SortableItem> items)
{
items.Sort();
for (int i = 0; i < items.Count; i++)
Console.WriteLine("Item " + i + ": " + items[i].ToString());
}
}
If you want dynamic sort, you can use LINQ
var itemsOrderedByNumber = ( from item in GetClasses() orderby item.Number select item ).ToList();
var itemsOrderedByText = ( from item in GetClasses() orderby item.Text select item ).ToList();
var itemsOrderedByDate = ( from item in GetClasses() orderby item.Date select item ).ToList();
or "Sort" method of List class:
List<Class1> itemsOrderedByNumber2 = new List<Class1>( GetClasses() );
itemsOrderedByNumber2.Sort( ( a, b ) => Comparer<int>.Default.Compare( a.Number, b.Number ) );
List<Class1> itemsOrderedByText2 = new List<Class1>( GetClasses() );
itemsOrderedByText2.Sort( ( a, b ) => Comparer<string>.Default.Compare( a.Text, b.Text ) );
List<Class1> itemsOrderedByDate2 = new List<Class1>( GetClasses() );
itemsOrderedByDate2.Sort( ( a, b ) => Comparer<DateTime>.Default.Compare( a.Date, b.Date ) );
You can use this for sorting list
namespace GenaricClass
{
class Employee :IComparable<Employee>
{
public string Name { get; set; }
public double Salary { get; set; }
public int CompareTo(Employee other)
{
if (this.Salary < other.Salary) return 1;
else if (this.Salary > other.Salary) return -1;
else return 0;
}
public static void Main()
{
List<Employee> empList = new List<Employee>()
{
new Employee{Name="a",Salary=140000},
new Employee{Name="b",Salary=120000},
new Employee{Name="c",Salary=160000},
new Employee{Name="d",Salary=10000}
};
empList.Sort();
foreach (Employee emp in empList)
{
System.Console.Write(emp.Salary +",");
}
System.Console.ReadKey();
}
}
}
This might not be in relation to sorting order, but it is still - I think - an interesting use of IComparable:
public static void MustBeInRange<T>(this T x, T minimum, T maximum, string paramName)
where T : IComparable<T>
{
bool underMinimum = (x.CompareTo(minimum) < 0);
bool overMaximum = (x.CompareTo(maximum) > 0);
if (underMinimum || overMaximum)
{
string message = string.Format(
System.Globalization.CultureInfo.InvariantCulture,
"Value outside of [{0},{1}] not allowed/expected",
minimum, maximum
);
if (string.IsNullOrEmpty(paramName))
{
Exception noInner = null;
throw new ArgumentOutOfRangeException(message, noInner);
}
else
{
throw new ArgumentOutOfRangeException(paramName, x, message);
}
}
}
public static void MustBeInRange<T>(this T x, T minimum, T maximum)
where T : IComparable<T> { x.MustBeInRange(minimum, maximum, null); }
These simple extension methods allow you to do parameter range checking for any type that implements IComparable like this:
public void SomeMethod(int percentage, string file) {
percentage.MustBeInRange(0, 100, "percentage");
file.MustBeInRange("file000", "file999", "file");
// do something with percentage and file
// (caller will have gotten ArgumentOutOfRangeExceptions when applicable)
}
using System;
using System.Collections.Generic;
using System.Text;
namespace Sorting_ComplexTypes
{
class Program
{
static void Main(string[] args)
{
Customer customer1 = new Customer {
ID = 101,
Name = "Mark",
Salary = 2400,
Type = "Retail Customers"
};
Customer customer2 = new Customer
{
ID = 102,
Name = "Brian",
Salary = 5000,
Type = "Retail Customers"
};
Customer customer3 = new Customer
{
ID = 103,
Name = "Steve",
Salary = 3400,
Type = "Retail Customers"
};
List<Customer> customer = new List<Customer>();
customer.Add(customer1);
customer.Add(customer2);
customer.Add(customer3);
Console.WriteLine("Before Sorting");
foreach(Customer c in customer)
{
Console.WriteLine(c.Name);
}
customer.Sort();
Console.WriteLine("After Sorting");
foreach(Customer c in customer)
{
Console.WriteLine(c.Name);
}
customer.Reverse();
Console.WriteLine("Reverse Sorting");
foreach (Customer c in customer)
{
Console.WriteLine(c.Name);
}
}
}
}
public class Customer : IComparable<Customer>
{
public int ID { get; set; }
public string Name { get; set; }
public int Salary { get; set; }
public string Type { get; set; }
public int CompareTo(Customer other)
{
return this.Name.CompareTo(other.Name);
}
}