Say we have a UI and in this UI we have a dropdown. This dropdown is filled with the translated values of an enum.
Bow, we have the possibility to sort by the int-value of the enum, by the name of the enum, and by the translated name of the enum.
But what if we want a different sorting than the 3 mentioned above. how to handle such a requirement?
Implement your own IComparer:
using System;
using System.Collections.Generic;
namespace test {
class Program {
enum X {
one,
two,
three,
four
}
class XCompare : IComparer<X> {
public int Compare(X x, X y) {
// TBA: your criteria here
return x.ToString().Length - y.ToString().Length;
}
}
static void Main(string[] args) {
List<X> xs = new List<X>((X[])Enum.GetValues(typeof(X)));
xs.Sort(new XCompare());
foreach (X x in xs) {
Console.WriteLine(x);
}
}
}
}
You can use the Linq extension OrderBy, and perform whatever comparison magic you want:
// order by the length of the value
SomeEnum[] values = (SomeEnum[])Enum.GetValues(typeof(SomeEnum));
IEnumerable<SomeEnum> sorted = values.OrderBy(v => v.ToString().Length);
Then wrap the different sorting alternatives into methods, and invoke the right one based on user preferences/input.
IEnumerable<T>.OrderBy(Func<T, TKey>, IComparer<TKey>)
Sort FileSystemRights enum using Linq and bind to WinForms comboBox:
comboBox1.DataSource = ((FileSystemRights[])Enum.GetValues(typeof(FileSystemRights))).
OrderBy(p => p.ToString()).ToArray();
Perhapse you could create an extension method for the Enum class, like this:
... first the declaration...
public enum MyValues { adam, bertil, caesar };
...then in a method...
MyValues values = MyValues.adam;
string[] forDatabinding = values.SpecialSort("someOptions");
...The extension method...
public static class Utils
{
public static string[] SpecialSort(this MyValues theEnum, string someOptions)
{
//sorting code here;
}
}
And you could add different parameters to the extension metod for different sort options etc.
Related
I'm desperately trying to delete all the items with a list of the same value inside.
Here's the code:
private void Button_deleteDouble_MouseDown(object sender, EventArgs e)
{
boardGenerate.Add(new BoardInformation(146, new List<string> { "test" }));
boardGenerate.Add(new BoardInformation(545, new List<string> { "test" }));
boardGenerate = boardGenerate.DistinctBy(x => x.positionQueen).ToList();
}
Normally, since the two lists inside the object are the same, the .DistinctBy() command should remove one of the two objects.
But no, my object list still has the same two objects with the same list
.positionQueen is the name of the variable containing the list
Could somebody help me?
Edit :
The DistinctBy() method comes from MoreLinq.
And this is my BoardInformation class:
public class BoardInformation
{
public BoardInformation(int nbQueen, List<string> positionQueen)
{
this.nbQueen = nbQueen;
this.positionQueen = positionQueen;
}
public int nbQueen { get; set; }
public List<string> positionQueen { get; set; }
}
Set-based operations like Distinct and DistinctBy need a way of determining whether two values are the same. You're using DistinctBy, so you're already asking MoreLINQ to compare the "inner lists" for equality - but you're not saying how to do that.
List<T> doesn't override Equals or GetHashCode, which means it inherits the reference equality behaviour from System.Object. In other words, if you create two separate List<T> objects, they won't compare as equal, even if they have the same content. For example:
List<int> list1 = new List<int>();
List<int> list2 = new List<int>();
Console.WriteLine(list1.Equals(list2)); // False
You need to tell DistinctBy how you want to compare the two lists, using an IEqualityComparer<T> - where T in this case is List<string> (because that's the type of BoardInformation.positionQueen.
Here's an example of a generic ListEqualityComparer you could use:
using System;
using System.Collections.Generic;
using System.Linq;
public sealed class ListEqualityComparer<T> : IEqualityComparer<List<T>>
{
private readonly IEqualityComparer<T> elementComparer;
public ListEqualityComparer(IEqualityComparer<T> elementComparer) =>
this.elementComparer = elementComparer;
public ListEqualityComparer() : this(EqualityComparer<T>.Default)
{
}
public bool Equals(List<T> x, List<T> y) =>
ReferenceEquals(x, y) ? true
: x is null || y is null ? false
// Delegate to LINQ's SequenceEqual method
: x.SequenceEqual(y, elementComparer);
public int GetHashCode(List<T> obj)
{
if (obj is null)
{
return 0;
}
// Just a very simple hash implementation
int hash = 23;
foreach (var item in obj)
{
hash = hash * 31 +
(item is null ? 0
: elementComparer.GetHashCode(item));
}
return hash;
}
}
You'd then pass that to DistinctBy, like this:
// We're fine to use the default *element* comparer (string.Equals etc)
var comparer = new ListEqualityComparer<string>();
boardGenerate = boardGenerate.DistinctBy(x => x.positionQueen, comparer).ToList();
Now DistinctBy will call into the comparer, passing in the lists, and will consider your two BoardInformation objects are equal - so only the first will be yielded by DistinctBy, and you'll end up with a list containing a single item.
It comes down to whether a equality check is using referential equality or value equality...you want value equality based on a specific property and that has to be done by hand.
When there is no IEqualityComparer provided which can used to compare individual objects (which is need by the Distinct call), the system determines the equality from each item's references by using their derived object low level service method call of GetHashCode from each reference; hence a reference difference is done and all your values in the list are unique (not equal) regardless of similar property values.
What you are looking for is to have value equality checked specifically for the nbQueenProperty.
To fully utilize Distinct one must create a IEqualityComparer and modify the GetHashCode. By specifing the hash value which can make objects equal...you can weed out the same positionQueen (or other properties) instances out.
Example
public class MyClass
{
public string Name { get; set; }
public int nbQueen { get; set; }
}
Equality comparer to weed out all nbQueen similarities:
class ContactEmailComparer : IEqualityComparer < MyClass >
{
public bool Equals(MyClass x, MyClass y)
{
return x.nbQueen.Equals(y.nbQueen); // Compares by calling each `GetHashCode`
}
public int GetHashCode(MyClass obj)
{
return obj.nbQueen.GetHashCode(); // Add or remove other properties as needed.
}
}
Test code
var original = new List<MyClass>()
{
new MyClass() { nbQueen = 1, Name="Alpha" },
new MyClass() { nbQueen = 1, Name="Omega" },
new MyClass() { nbQueen = 3, Name="Delta" }
};
IEqualityComparer<MyClass> comparer = new ContactEmailComparer();
var newOne = original.Distinct( comparer ).ToList();
Result of the value of newOne :
To be clear...
... .DistinctBy() command should remove one of the two objects.
Does not remove anything. It returns a reference to a new list that should be distinct via the equality operation. The original list (the reference to it) does not change.
LINQ solution
because you have another List inside your class you can not use District or DistrictBy, alternatively, you can use LINQ to filter the list.
boardGenerate = (from b in boardGenerate
from l in b.positionQueen
group new { l,b } by l into g
select g.First().b
).ToList();
// this returns just first duplicate item like district
I am trying to sort an ArrayList using c#. When the ArrayList contains comparable objects, it is possible to sort with using list.Sort() but I need to sort an ArrayList which contains non-comparable objects. For example, let's say the object is Ring and it has an attribute property Price. Then I need to sort the ArrayList to the price order. If is is possible to select ascending or descending that will more helpful. Thank You!
Blockquote
arrAtdMon = **(ArrayList)**hashTb[unixMon];
if (arrAtdMon != null)
monCount = arrAtdMon.Count;
int[] arrayMax = { monCount, tueCount, wedCount, thuCount, friCount };
int maxValue = arrayMax.Max();
KidAttendance valMon = null;
string monTagName = string.Empty;
Blockquote
above array list is to be sorted it self.
You can do this by implementing IComparer interface:-
public class Ring : IComparer
{
public decimal Price { get; set; }
public int Compare(object x, object y)
{
return ((Ring)x).Price.CompareTo(((Ring)y).Price);
}
}
Working Fiddle.
First, you really should be using the List<T> class, not ArrayList. Doing so wouldn't solve your problem, but it would make the code less fragile and more easy to maintain.
As for the specific question, you want to do something like this…
Assume:
class Ring { public decimal Price { get; set; } }
Then:
ArrayList list = ...; // Initialized as some collection of Ring instances
list.Sort(Comparer.Create((r1, r2) => r1.Price.CompareTo(r2.Price)));
This creates a new Comparer instance using the Comparison<T> of (r1, r2) => r1.Price.CompareTo(r2.Price). That is, for each pair of objects being compared, compare the price of the first with the price of the second.
Assuming that these objects share a base class or an interface with the price property you should be able to do something like this:
// Base class with price property, could also be an shared interface
public abstract class Product
{
public decimal Price{get;set;}
}
public class Ring : Product
{
}
public class Bag : Product
{
}
// Some test data
var myUnsortedArray = new Product[]{new Ring{Price = 1.2m}, new Bag{Price=2.5m}};
// Easy sort with LINQ
var sortedProducts = myUnsortedArray.OrderBy(p => p.Price).ToArray();
var sortedProductsDescending = myUnsortedArray.OrderByDescending(p => p.Price).ToArray();
UPDATE
I just realised that the question is about ArrayLists and have the changed solution below:
// Some test data
var myUnsortedArrayList = new ArrayList{new Ring{Price = 1.2m}, new Bag{Price=2.5m}};
// Easy sort with LINQ
var sortedProducts = myUnsortedArrayList.OfType<Product>().OrderBy(p => p.Price).ToArray();
var sortedProductsDescending = myUnsortedArrayList.OfType<Product>().OrderByDescending(p => p.Price).ToArray();
To sort an set of objects, the object needs to be Comparable and you can set up the comparison you'd like in the CompareTo() method:
IComparable information here
Scenario:
I Have a table FooTable when column Foo is varchar(8) NOT NULL and the info in this column is like:
Foo
-----------
13940-00
13940-01
13940-02
13941-00
13941-01
Where the numeric part after the hyphen (-) always have two digits.
Problem:
I'm using Ado.net Entity Framework, and I created a class with 2 static methods to help get first and second part of the number:
public class HelperFoo
{
public static string Prefix(string value) { /* code here */ }
public static string Suffix(string value) { /* code here */ }
}
So now I can do something like this:
context.FooTable.Where(w => HelperFoo.Prefix(w.Foo) == "13940");
But, as you probably already know, this command line throws a NotSupportedException. It's because LINQ don't recognize HelperFoo.Prefix, so it can't convert the expression in SQL.
I can write a block of code in SQL that do the same that my methods of HelperFoo so I can create the SQL to my methos.
Question
Can I create something (class, method, or other) that makes LINQ knows my method when I executed the LINQ code?
EDITED
PS: I need something generic that works like a method or SQL function because I need to get this Prefix and Suffix in scenarios like Select, OrderBy, GroupBy and many others.
You could try creating your own IQueryable filters for the FooTable, a bit like this:
public static class Filters
{
public static IQueryable<FooTable> WithPrefix(this IQueryable<FooTable> item, string prefix)
{
return item.Where(i => i.Foo.StartsWith(prefix));
// note that this should be the same code you have in the
// Prefix() method inside HelperFoo...
}
}
Which you can use like this:
context.FooTable.WithPrefix("13940");
UPDATE: Sadly the second option here does not work:
Another option would be to have the helper methods not return a value but a Predicate<> for FooTable:
public class HelperFoo
{
public static Func<FooTable, bool> Prefix(string value)
{
return (i) => i.Foo.Substring(0, 5) == value;
}
public static Func<FooTable, bool> Suffix(string value) { /* code here */ }
}
And use it like this:
context.FooTable.Where(HelperFoo.Prefix("13940"));
Caveat: I'm not entirely sure the second method would not give you the same problem though.
With the the plethera of awnsers and you stating it needs to be more generic and you need the prefix and suffix avaliable to the Select, OrderBy, & GroupBy keywords, you should have the prefix and suffix in two different fields.
Foo Table
----------
Prefix | Suffix
----------------
10245 | 05
With that, you can query them individually to get what you want:
var resultSet = Db.Foo.Where(x => x.Suffix == "05").OrderBy(x => x.Prefix);
With this you can easily add a read-only property to get a formatted value:
public [partial] class Foo {
//Your other code
public string FormattedValue {
get { return Prefix + "-" + Suffix; }
}
}
You can use .StartsWith to check the prefix:
string prefix = "13940";
var result = context.FooTable.Where(w => w.Foo.StartsWith(prefix + "-"));
and .EndsWith to check the suffix:
string suffix = "02";
var result = context.FooTable.Where(w => w.Foo.EndsWith("-" + suffix));
You could create custom getters in your Foo class:
public class Foo
{
//your code
[NotMapped]
public string Prefix { get { return /*whatever*/; }
}
I think this should work.
You cannot filter your database selection based on a custom function - as you say, it cannot convert this to SQL.
For this specific problem, I could propose you use the StartsWith function, which does work on SQL server
context.FooTable.Where(w => w.Foo.StartsWith("13940"));
Use Microsoft.Linq.Translations.
It would look something like this:
partial class FooTable
{
private static readonly CompiledExpression<FooTable,string> prefixExpression
= DefaultTranslationOf<FooTable>.Property(e => e.Prefix).Is(e => e.Foo.Substring(0, 5));
public string Prefix
{
get { return prefixExpression.Evaluate(this); }
}
}
And queried like:
context.FooTable.Where(w => w.Prefix == "13940").WithTranslations();
Nuget gallery page
Documentation
EDIT: This solution works in Select, GroupBy, OrderBy.
I have the following enum:
public enum SymbolWejsciowy
{
K1 , K2 , K3 , K4 , K5 , K6 , K7 , K8
}
I want to create a list using the values of this enum:
public List<SymbolWejsciowy> symbol;
I have tried a couple different ways to add the enum values to the list:
SymbolWejsciowy symbol;
symbol.Add(symbol = SymbolWejsciowy.K1);
and
symbol.Add(SymbolWejsciowy.K1);
However, I always get the following exception:
Object reference not set to an instance of an object.
How can I correctly accomplish this?
As other answers have already pointed out, the problem is that you have declared a list, but you haven't constructed one so you get a NullReferenceException when you try to add elements.
Note that if you want to construct a new list you can use the more concise collection initializer syntax:
List<SymbolWejsciowy> symbols = new List<SymbolWejsciowy>
{
SymbolWejsciowy.K1,
SymbolWejsciowy.K2,
// ...
};
If you want a list containing all the values then you can get that by calling Enum.GetValues:
List<SymbolWejsciowy> symbols = Enum.GetValues(typeof(SymbolWejsciowy))
.Cast<SymbolWejsciowy>()
.ToList();
In your option 1 SymbolWejsciowy instance and your list have the same name, I imagine that's a typo error.
Without taking that into account I'd say you didn't created the instance of the list
symbol = new List<SymbolWejsciowy>();
Your code never initializes the list. Try this:
public List<SymbolWejsciowy> symbol = new List<SymbolWejsciowy>();
symbol.Add(SymbolWejsciowy.K1);
and
SymbolWejsciowy mySymbol= SymbolWejsciowy.K2;
symbol.Add(mySymbol);
It sure would be nice if Enum.GetValues() had been updated for generics way back in C# 2.0. Well, guess we have to write it ourselves:
static class EnumHelpers<T> where T : struct
{
public class NotAnEnumException : Exception
{
public NotAnEnumException() : base(string.Format(#"Type ""{0}"" is not an Enum type.", typeof(T))) { }
}
static EnumHelpers()
{
if (typeof(T).BaseType != typeof(Enum)) throw new NotAnEnumException();
}
public static IEnumerable<T> GetValues()
{
return Enum.GetValues(typeof(T)).Cast<T>();
}
public static T Parse(string value)
{
return (T)Enum.Parse(typeof(T), value);
}
}
I included Parse() because it benefits from generics in the same way.
Usage:
var symbols = EnumHelpers<SymbolWejsciowy>.GetValues().ToList();
SymbolWejsciowy s = EnumHelpers<SymbolWejsciowy>.Parse("S2");
(ASIDE: I also wish you could write where T : enum for just this sort of thing. Also, where T : delegate.)
None of these answers worked for me.
I think most people just want a List<string> or list of values after combining many enums together. This should help:
static class MyPets {
enum Cats
{
Felix,
Hairy,
TunaBreath
}
enum Dogs
{
Fido,
Fred,
Butch
}
public static void PrintPets() {
List<string> Pets = new List<string>();
Pets.AddRange(Enum.GetNames(typeof(Cats)).ToList());
Pets.AddRange(Enum.GetNames(typeof(Dogs)).ToList());
foreach(string p in Pets){
Console.WriteLine(p);
}
}
}
// RESULT
Felix
Hairy
TunaBreath
Fido
Fred
Butch
How can I sort a List by order of case e.g.
smtp:user#domain.com
smtp:user#otherdomain.com
SMTP:user#anotherdomain.com
I would like to sort so that the upper case record is first in the list e.g SMTP:user#anotherdomain.com.
You can use StringComparer.Ordinal to get a case sensitive sorting:
List<string> l = new List<string>();
l.Add("smtp:a");
l.Add("smtp:c");
l.Add("SMTP:b");
l.Sort(StringComparer.Ordinal);
I was writing another example while t4rzsan has answered =) I prefer t4rzsan´s answer... anyway, this is the answer I was writing.
//Like ob says, you could create your custom string comparer
public class MyStringComparer : IComparer<string>
{
public int Compare(string x, string y)
{
// Return -1 if string x should be before string y
// Return 1 if string x should be after string y
// Return 0 if string x is the same string as y
}
}
Example of using your own string comparer:
public class Program
{
static void Main(string[] args)
{
List<string> MyList = new List<string>();
MyList.Add("smtp:user#domain.com");
MyList.Add("smtp:user#otherdomain.com");
MyList.Add("SMTP:user#anotherdomain.com");
MyList.Sort(new MyStringComparer());
foreach (string s in MyList)
{
Console.WriteLine(s);
}
Console.ReadLine();
}
}
Most language libraries have a built in sort function with a way to specify the compare function. You can customize the compare function to sort based on any criteria you want.
In your case the default sort function will probably work.
you need to create a custom comparer class that implements IComparer