Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations Mike Lewis on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Need help with generics 2

Status
Not open for further replies.

kss444

Programmer
Sep 19, 2006
306
US
I have inherited this web app; that stores alot of info in collections; And it sorts it's collections using generics (I think).

Well now I need to sort by multiple fields and I don't know how to do this with my generic functions.
I need to sort the collection by price, then by day's then by name all asc. The following is the collection class that has the sort functionality.
Any help would be welcome.
Thanks,

public static bool operator <(RateQuote r1, RateQuote r2)
{
Decimal d1 = 0, d2 = 0;
String s1 = (r1 != null && r1.Actual_Price != null) ?
r1.Actual_Price.Replace("$", "") :
String.Empty;

String s2 = (r2 != null && r2.Actual_Price != null) ?
r2.Actual_Price.Replace("$", "") :
String.Empty;

Decimal.TryParse(s1, out d1);
Decimal.TryParse(s2, out d2);
return d1 < d2;
}

public static bool operator <=(RateQuote r1, RateQuote r2)
{
return (!(r1 > r2));
}

public static bool operator >(RateQuote r1, RateQuote r2)
{
Decimal d1 = 0, d2 = 0;
String s1 = (r1 != null && r1.Actual_Price != null) ?
r1.Actual_Price.Replace("$", "") :
String.Empty;

String s2 = (r2 != null && r2.Actual_Price != null) ?
r2.Actual_Price.Replace("$", "") :
String.Empty;

Decimal.TryParse(s1, out d1);
Decimal.TryParse(s2, out d2);
return d1 > d2;
}

public static bool operator >=(RateQuote r1, RateQuote r2)
{
return (!(r1 < r2));
}

#endregion

#region IComparable

/// <summary>
/// Implements Systems.Collections.Generic.IComparable&lt;Rate&gt;.
/// </summary>
/// <param name="rate2">
/// The Rate against which this Rate will be compared
/// </param>
/// <returns>
/// -1 if this Rate is the lesser.
/// 1 if this Rate is the greater.
/// else 0.
/// </returns>
public int CompareTo(RateQuote rate2)
{
return ((this < rate2 ) ? -1 : (this > rate2) ? 1 : 0);
}

#endregion
}

/// <summary>
/// Defines a collection of Rate objects.
/// </summary>
public class RateQuoteCollection : List<RateQuote>
{

}

Ordinary Programmer
 
wow this is a mess.

1st, I would do away with the operators. they are syntax sugar, but very restrictive.

2nd, RateQuote.ActualPrice appears to be a formatted (currency) string. this is useless for sorting. The only layer that should care about formatting is the GUI. until it's actually presented to the user it's just data. I would change ActualPrice to a decimal and refactor code that's broken.

now for sorting. if you are using .net 3.5 you can use Linq to easily sort your objects. OrderBy().ThenBy().ThenByDescending()

if you're not you will need to build your sort operators.
the simplest approach is brute force with delegates
Code:
List<Foo> foos = GetListOfFoo();
foos.Sort(delegate(Foo x, Foo y)
   {
       var equal = x.Price.CompareTo(y.Price);
       if(equal == 0) return x.Name.CompareTo(y.Name);
       return equal;
   });
return foos;
the middle ground is to create a set of IComparable implementations and wire them together
Code:
class FooPriceComparer: IComparable<Foo>
{
   public int CompareTo(Foo x, Foo y)
   {
       return x.Price.CompareTo(y.Price);
   }
}
class FooNameComparer: IComparable<Foo>
{
   public int CompareTo(Foo x, Foo y)
   {
       return x.Name.CompareTo(y.Name);
   }
}
class MultiSort<T>: IComparable<T>
{
   IComparable<T> first;
   IComparable<T> second;

   public MultiSort(IComparable<T> first, IComparable<T> second)
   {
       this.first = first;
       this.second = second;
   }

   public int CompareTo(T x, T y)
   {
       int result = first(x, y);
       return (result == 0) ? second(x, y) : result;
   }
}
class Descending<T>: IComparable<T>
{
   IComparable<T> original;

   public Descending(IComparable<T> original)
   {
       this.original = original;
   }

   public int CompareTo(T x, T y)
   {
       return -original(x, y);
   }
}
which will produce the following
Code:
List<Foo> foos = GetListOfFoo();
foos.Sort(new MultiSort(new FooPriceComparer(), new FooNameComparer()));
return foos;
this is more dynamic but requires more objects.

You could go all out and build a sorting framework which could produce
Code:
List<Foo> foos = GetListOfFoo();
foos.Sort(Order<Foo>.By(f=>f.Price).Descending().ThenBy(f=>f.Name));
return foos;
which is more code than I can provide right now:)
Hopefully this gets you thinking of the possibilities.

Jason Meckley
Programmer
Specialty Bakers, Inc.

faq855-7190
 
Great, this gives me a great start.
Thank you Jason.


Ordinary Programmer
 
I would be very interested in seeing a LINQ version, if it is easier to use and understand.

I have a variable rates created from new ratequotecollection()

then I create a variale rate from new ratequote()

Then I loop through a recordset brought back from a vb6 com interop we have,
and populate rate.actualprice, rate.days, rate.name ect... until recordset.EOF
and in the loop I am adding rate to rates.

once done with the loop I do a rates.Sort().
And here is were I now need to sort by actualprice, days, name

I tried:
rates = from r in rates
orderby r.Actual_Price,r.Days,r.Carrier.ToCharArray() descending
select r;

but I get a conversion error on any value in my orderby:
cannot convert type ratequote to ratequotecollection.

Ordinary Programmer
 
Code:
rates = from r in rates
   orderby r.Actual_Price,r.Days,r.Carrier[COLOR=red].ToCharArray()[/color] descending
   select r;
you are trying to sort a char[] descending for rates. this doesn't make any sense. just sort the Carrier property (assuming it's a string).

Jason Meckley
Programmer
Specialty Bakers, Inc.

faq855-7190
 
Sorry about that I have been playing around with it.
I have:

rates = from r in rates
orderby r.Actual_Price,r.Days,r.Carrier descending
select r;

And they are all strings, but I get a conversion error on any value in my orderby:
Error 240 Cannot implicitly convert type 'System.Linq.IOrderedEnumerable<RateQuote>' to 'RateQuoteCollection'. An explicit conversion exists (are you missing a cast?)

Ordinary Programmer
 
is rates an array of rate quotes RateQuote[]?
if so you need to cast the enumerable to an array.
Code:
RateQuote [] rates = (from r in rates
   orderby r.Actual_Price,r.Days,r.Carrier descending
   select r)
   .ToArray();
which is the same as
Code:
[code]
RateQuote [] rates = rates
   .OrderBy(r=>r.Actual_Price)
   .ThenBy(r=>r.Days)
   .ThenByDescending(r=>r.Carrier)
   .ToArray();

Jason Meckley
Programmer
Specialty Bakers, Inc.

faq855-7190
faq732-7259
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top