Windows Develop Bookmark and Share   
 index > Windows Forms General > ArrayList.sort()
 

ArrayList.sort()

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 Software
Coding 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 Software
Coding 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 Software
Coding 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 Software
Coding 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 Software
Coding 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

Right now, you're still using OrderBy.

Change it to:

MyList.Sort();


Coding Light - Illuminated Ideas and Algorithms in Software
Coding Light Wiki �Twitter �LinkedIn �ForumsBrowser
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 Software
Coding 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

using System.Linq;
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 Software
Coding 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

You can use google to search for other answers

Custom Search

More Threads

• Keep Dialog Open until validated
• Help with Timer control
• How to set controls to adapt form size?
• Can I set specified folder when I open saveas dialog in WebBrowser control using "ShowSaveAsDialog"?
• FlowLayoutPanel and mouse wheel
• Dialog Box in different OS
• Custom DateTimePIcker with no drop down
• ERROR: Exception generated when setting ComboBox.SelectedItem
• Exception thrown when changing opacity
• controls library