|
I am building an application in which I have an ArrayList that has classes for its elements. I am doing it this way because I need to store several fields for each element. How do I make theArrayList.sort() work?
It works fine if the array contains a single field, but I need it to sort on a field within the class that makes up its elements. | | JimGuyer Wednesday, August 26, 2009 5:36 PM | You have to inherit from IComparable and implement CompareTo: public class ClassCar: IComparable { private string PrvName; public int PblYear; public Color PblColor; #region Constructor public ClassCar( string ParmName, int ParmYear, Color ParmColor) { PrvName = ParmName; PblYear = ParmYear; PblColor = ParmColor; } #endregion public string Name { get { return PrvName; } } public int CompareTo(object other) { if (other is ClassCar) { ClassCar car = (ClassCar)other; return this.PblYear.CompareTo(car.PblYear); } else { throw new ArgumentException("other is not aClassCar!"); } } } That being said, look into Hans' response as well. Both are good options. Hans is a little more configurable if you're wanting to do a different kind of sort later on.Implementing IComparable is good if you only ever want to sort it one way. In addition. You should make properties public and fields private in this situation, and then eliminate the "Pbl" and "Prv" before your variable names. Gain some consistency in how you name them to indicate if their public or private. For example, make the private variable name be something like: _name And the public property name: Name This is standard convention in C#, and will help your code be more readable to other developers.
Coding Light - Illuminated Ideas and Algorithms in SoftwareCoding Light Wiki � Twitter � LinkedIn � ForumsBrowser- Marked As Answer byJimGuyer Wednesday, August 26, 2009 8:15 PM
-
| | David M Morton Wednesday, August 26, 2009 6:22 PM | Jim, Why not use a generic List<T> instead of an ArrayList? ArrayList hasn't been in vogue since .NET 1.1, before it was superceded by List<T>. To use the ArrayList.Sort, you either have to implement IComparable on the object, and handle the comparison in the CompareTo method, or you can create another class, and implement IComparer overriding the Compare class and implementing the logic there. Both of these, IMHO, are a bit much. Another way you can do it is to create a brand new array using LINQ, and use the .OfType<T>() method to create an IEnumerable<T> where T is the type of your object. It would look like this: arrayList = new ArrayList(arrayList.OfType<MyType>().OrderBy(x => x.PropertyToSortOn).ToArray()); I would strongly recommend this approach, however. You'll do far better to implement List<T> and make your list a strongly typed collection than to ever use ArrayList again. Coding Light - Illuminated Ideas and Algorithms in SoftwareCoding Light Wiki � Twitter � LinkedIn � ForumsBrowser | | David M Morton Wednesday, August 26, 2009 5:43 PM | OK, I will give List<T> a shot. Can you give me a code example for List<T>? | | JimGuyer Wednesday, August 26, 2009 5:54 PM | List<MyObject> list = new List<MyObject>(); list = list.OrderBy(x => x.PropertyToSortBy).ToList(); Coding Light - Illuminated Ideas and Algorithms in SoftwareCoding Light Wiki � Twitter � LinkedIn � ForumsBrowser | | David M Morton Wednesday, August 26, 2009 5:57 PM | Linq can be quite dangerous, can't always see the man in the box. That's a sort that has O(n) storage requirements. Sort in-place with System.Comparison: private struct MyStruct { public int a, b; } var lst = new List<MyStruct>(); //... // Sort by field b lst.Sort(new Comparison<MyStruct>((x, y) => x.b - y.b));
Hans Passant. | | nobugz Wednesday, August 26, 2009 6:17 PM | Hi Dave, Thanks, I will try that and post the results. I am a little weak on Lambda's so I may struggle a little. While waiting for your next reply, I hadbeen trying to get more proficent in the useage of List<> Here is what I don't understand. List has a List<>.sort But when I try to use it, I get {"Failed to compare two elements in the array."} Is the List.Sort funtionality not useable? I think I remember sonething about putting a "CompareTo" method in the class or something like that, but don't remember how that was done.
class Program {
static void Main() {
//Declare and define ArrayList
List<ClassCar> MyList;
MyList = new List<ClassCar>();
//Adding individual elements
MyList.Add(new ClassCar("Opel", 1970, Color.Gold));
MyList.Add(new ClassCar("Dodge", 1970, Color.Green));
MyList.Add(new ClassCar("Celica", 1977, Color.Blue));
MyMethodWriteList(MyList);
<strong>MyList.Sort();</strong>
MyMethodWriteList(MyList);
}
static void MyMethodWriteList(List<ClassCar> ParmList) {
//Console.WriteLine("\nWrite using a for loop");
//object[] ObjList = ParmList.ToArray();
//for (int EnumCar = 0; EnumCar < ObjList.Length; EnumCar++) {
// Console.WriteLine("Name={0}, \tYear={1}, \tColor={2}", ((ClassCar)ObjList[EnumCar]).Name, ((ClassCar)ObjList[EnumCar]).PblYear, ((ClassCar)ObjList[EnumCar]).PblColor);
//}
Console.WriteLine("\nWrite using a foreach loop");
foreach (ClassCar EnumCar in ParmList) {
Console.WriteLine("Name={0}, \tYear={1}, \tColor={2}", EnumCar.Name, EnumCar.PblYear, (EnumCar.PblColor));
}
Console.WriteLine("\nNumber of elements in the queue = {0}", ParmList.Count);
Console.ReadLine();
}
}
public class ClassCar {
private string PrvName;
public int PblYear;
public Color PblColor;
#region Constructor
public ClassCar(string ParmName, int ParmYear, Color ParmColor) {
PrvName = ParmName;
PblYear = ParmYear;
PblColor = ParmColor;
}
#endregion
public string Name {
get { return PrvName; }
}
}
| | JimGuyer Wednesday, August 26, 2009 6:18 PM | The containing type needs to implement IComparable<T> for List<> to be able to sort it. Otherwise use an overload that lets you pass a comparison mechanism as a delegate. http://blog.voidnish.com | | Nishant Sivakumar Wednesday, August 26, 2009 6:21 PM | You have to inherit from IComparable and implement CompareTo: public class ClassCar: IComparable { private string PrvName; public int PblYear; public Color PblColor; #region Constructor public ClassCar( string ParmName, int ParmYear, Color ParmColor) { PrvName = ParmName; PblYear = ParmYear; PblColor = ParmColor; } #endregion public string Name { get { return PrvName; } } public int CompareTo(object other) { if (other is ClassCar) { ClassCar car = (ClassCar)other; return this.PblYear.CompareTo(car.PblYear); } else { throw new ArgumentException("other is not aClassCar!"); } } } That being said, look into Hans' response as well. Both are good options. Hans is a little more configurable if you're wanting to do a different kind of sort later on.Implementing IComparable is good if you only ever want to sort it one way. In addition. You should make properties public and fields private in this situation, and then eliminate the "Pbl" and "Prv" before your variable names. Gain some consistency in how you name them to indicate if their public or private. For example, make the private variable name be something like: _name And the public property name: Name This is standard convention in C#, and will help your code be more readable to other developers.
Coding Light - Illuminated Ideas and Algorithms in SoftwareCoding Light Wiki � Twitter � LinkedIn � ForumsBrowser- Marked As Answer byJimGuyer Wednesday, August 26, 2009 8:15 PM
-
| | David M Morton Wednesday, August 26, 2009 6:22 PM | When the list contains objects, it would have no idea how you would want to sort. Do you want to sort on year? On color? On name? Look at the other sort overloads. Hope this helps. www.insteptech.com ;
msmvps.com/blogs/deborahk
We are volunteers and ask only that if we are able to help you, that you mark our reply as your answer. THANKS! | | DeborahK Wednesday, August 26, 2009 6:23 PM | I tried it, by I am getting Error1'System.Collections.Generic.List<MyConsoleApplication.ClassCar>' does not contain a definition for 'OrderBy' and no extension method 'OrderBy' accepting a first argument of type 'System.Collections.Generic.List<MyConsoleApplication.ClassCar>' could be found (are you missing a using directive or an assembly reference?)C:\Documents and Settings\jim.guyer\My Documents\Visual Studio 2008\Projects\MyConsoleApplication\MyConsoleApplication\C034_ListClass.cs5229MyConsoleApplication
class Program {
static void Main() {
List<ClassCar> MyList;
MyList = new List<ClassCar>();
//Adding individual elements
MyList.Add(new ClassCar("Opel", 1970, Color.Gold));
MyList.Add(new ClassCar("Dodge", 1970, Color.Green));
MyList.Add(new ClassCar("Celica", 1977, Color.Blue));
MyMethodWriteList(MyList);
//MyList.Sort();
MyList = MyList.OrderBy(x => x.Name).ToList();
MyMethodWriteList(MyList);
);
}
static void MyMethodWriteList(List<ClassCar> ParmList) {
Console.WriteLine("\nWrite using a foreach loop");
foreach (ClassCar EnumCar in ParmList) {
Console.WriteLine("Name={0}, \tYear={1}, \tColor={2}", EnumCar.Name, EnumCar.PblYear, (EnumCar.PblColor));
}
Console.WriteLine("\nNumber of elements in the queue = {0}", ParmList.Count);
Console.ReadLine();
}
}
public class ClassCar {
private string PrvName;
public int PblYear;
public Color PblColor;
#region Constructor
public ClassCar(string ParmName, int ParmYear, Color ParmColor) {
PrvName = ParmName;
PblYear = ParmYear;
PblColor = ParmColor;
}
#endregion
public string Name {
get { return PrvName; }
}
}
| | JimGuyer Wednesday, August 26, 2009 6:24 PM | Are you using .NET 3.5? You should be if you have lambda statements. Add the following statement to the top of your file: using System.Linq; Coding Light - Illuminated Ideas and Algorithms in SoftwareCoding Light Wiki � Twitter � LinkedIn � ForumsBrowser | | David M Morton Wednesday, August 26, 2009 6:25 PM | You may need this : using System.Linq; And System.Core needs to be referenced.
http://blog.voidnish.com | | Nishant Sivakumar Wednesday, August 26, 2009 6:26 PM | I got it sort, but it will only sort by the first field (Name in this instance) Is there anyway to get it to sort by one of the other fields?
class Program {
static void Main() {
#region Console
Console.ForegroundColor = ConsoleColor.Green;
Console.WindowHeight = Console.LargestWindowHeight;
Console.WindowWidth = 100;
#endregion
List<ClassCar> MyList;
MyList = new List<ClassCar>();
//Adding individual elements
MyList.Add(new ClassCar("Opel", 1970, "Gold"));
MyList.Add(new ClassCar("Dodge", 1971, "Red"));
MyList.Add(new ClassCar("Celica", 1977, "Blue"));
MyMethodWriteList(MyList);
//MyList.Sort();
MyList = MyList.OrderBy(x => x.Name).ToList();
MyMethodWriteList(MyList);
}
static void MyMethodWriteList(List<ClassCar> ParmList) {
Console.WriteLine("\nWrite using a foreach loop");
foreach (ClassCar EnumCar in ParmList) {
Console.WriteLine("Name={0}, \tYear={1}, \tColor={2}", EnumCar.Name, EnumCar.Year, (EnumCar.Color));
}
Console.WriteLine("\nNumber of elements in the queue = {0}", ParmList.Count);
Console.ReadLine();
}
}
public class ClassCar : IComparable {
private string PrvName;
private int PrvYear;
private string PrvColor;
#region Constructor
public ClassCar(string ParmName, int ParmYear, string ParmColor) {
PrvName = ParmName;
PrvYear = ParmYear;
PrvColor = ParmColor;
}
#endregion
public string Name {get { return PrvName; }}
public int Year { get { return PrvYear; } }
public string Color { get { return PrvColor; } }
public int CompareTo(object other) {
if (other is ClassCar) {
ClassCar car = (ClassCar)other;
//return this.Name.CompareTo(car.Name);
//return this.Year.CompareTo(car.Year);
return this.Color.CompareTo(car.Color);
} else {
throw new ArgumentException("other is not a ClassCar!");
}
}
} | | JimGuyer Wednesday, August 26, 2009 6:57 PM | | | David M Morton Wednesday, August 26, 2009 7:00 PM | OK, I figured out what was wrong. I had not changed my OderBy Statement
MyList = MyList.OrderBy(x => x.Color).ToList();
Now, I have another question. It seems like, no matter what I put in the CompareTo Method
public int CompareTo(object other) {
if (other is ClassCar) {
ClassCar car = (ClassCar)other;
return this.Name.CompareTo(car.Name);
//return this.Year.CompareTo(car.Year);
//return this.Color.CompareTo(car.Color);
} else {
throw new ArgumentException("other is not a ClassCar!");
}
}
It always sorts on whatever is listed in the OderBy statement. | | JimGuyer Wednesday, August 26, 2009 7:06 PM | Yep. this is expected. Read the last post. If you're wanting to sort by CompareTo, then use the Sort method not the OrderBy method. If you want to make the Sort method dynamic, go with Hans suggestion, and define the generic Comparer<T> by using the lambda expression in the Comparer<T> constructor. Coding Light - Illuminated Ideas and Algorithms in SoftwareCoding Light Wiki � Twitter � LinkedIn � ForumsBrowser | | David M Morton Wednesday, August 26, 2009 7:10 PM | Ohhhhhhhhhhhhhhhh, I get it. The CompareTo works with the List.Sort and the OrderBy is all by its self. I was missing the part about that they are two different methods. Thanks so much folks. You are greatly incresing my understanding. One last question (If I haven't worn you out) With all syntax I have run across, instead of having a using statement, you could just prefix the command. If I comment out the
I was thinking I could use longer syntax on the OrderBy and it would still work.
MyList = MyList.Linq.OrderBy(x => x.Color).ToList();
But that doesn't work | | JimGuyer Wednesday, August 26, 2009 7:57 PM | OrderBy is an extension method. You could have done this though : System.Linq.Enumerable.OrderBy(myList, ... );
http://blog.voidnish.com | | Nishant Sivakumar Wednesday, August 26, 2009 8:45 PM | I tried that. It runs without error, but the sort doesn't work.
//MyList = MyList.OrderBy(x => x.Color).ToList();
System.Linq.Enumerable.OrderBy(MyList, x => x.Color).ToList();
| | JimGuyer Monday, August 31, 2009 10:01 PM | Jim,
You gotta save the result:
MyList= System.Linq.Enumerable.OrderBy(MyList, x => x.Color).ToList();
Then again, if you're able to use the "=>", then you shouldn't have any trouble simply calling:
MyList = MyList.OrderBy(x => x.Color).ToList();
Sort will sort the list in place. OrderBy creates a new IEnumerable<T> without modifying the list on which it was called. Coding Light - Illuminated Ideas and Algorithms in SoftwareCoding Light Wiki � LinkedIn � ForumsBrowser | | David M Morton Monday, August 31, 2009 10:03 PM | The first post in this thread got you on the wrong track. OrderBy().ToList() generates a new list, a copy of the old one but sorted. You have to assign it back: MyList = .....ToList(); Creating a copy of the list is very ugly. You should use the in-place List<>.Sort() method. Ask a direct question if you can't figure out how to use System.Comparison() as demonstrated in my previous post.
Hans Passant. | | nobugz Monday, August 31, 2009 10:08 PM | Thanks guys. The Lambada version is in and working, I just was trying to get a better understanding.
NoBugz, I have class as the generic for the list, and within it, are many fields. Can I still use the List<>sort? How will it know which filed to sort on? | | JimGuyer Tuesday, September 01, 2009 1:40 AM | Yes you can: MyList.Sort(new Comparison<CarClass>((x, y) => string.Compare(x.Color, y.Color)));
Hans Passant. | | nobugz Tuesday, September 01, 2009 2:00 AM | Thanks NoBugz, that works too. I appreciate you guys' help.
List<ClassCar> MyList;
MyList = new List<ClassCar>();
//Adding individual elements
MyList.Add(new ClassCar("Opel", 1970, "Gold"));
MyList.Add(new ClassCar("Dodge", 1970, "Green"));
MyList.Add(new ClassCar("Celica", 1977, "Blue"));
MyMethodWriteList(MyList);
MyList.Sort(new Comparison<ClassCar>((x, y) => string.Compare(x.Color, y.Color)));
//MyList = MyList.OrderBy(x => x.Name).ToList();
MyMethodWriteList(MyList);
}
static void MyMethodWriteList(List<ClassCar> ParmList) {
Console.WriteLine("\nWrite using a foreach loop");
foreach (ClassCar EnumCar in ParmList) {
Console.WriteLine("Name={0}, \tYear={1}, \tColor={2}", EnumCar.Name, EnumCar.Year, (EnumCar.Color));
}
Console.WriteLine("\nNumber of elements in the queue = {0}", ParmList.Count);
Console.ReadLine();
}
}
public class ClassCar {
private string PrvName;
private int PrvYear;
private string PrvColor;
#region Constructor
public ClassCar(string ParmName, int ParmYear, string ParmColor) {
PrvName = ParmName;
PrvYear = ParmYear;
PrvColor = ParmColor;
}
#endregion
public string Name { get { return PrvName; } }
public int Year { get { return PrvYear; } }
public string Color { get { return PrvColor; } }
}
}
| | JimGuyer Tuesday, September 01, 2009 2:00 PM |
|