1

if i have a generics list with more field for example:

PMyList = record 
  Field1, Field2, ... FieldN : Integer;
end;
TMyList = List<PMyList>;

For order the list with criteria choose to runtime (for example: field2 then field2, or: field3 then field1 then field2 etc) there is some solution or i need to do a compare construct for all combination possibile of order that i want?

Mine idea was, if record is N field, i have thinked to a array so defined:

MyArray = array [1..n] of Integer;

and assign a progressive value to elements of array that determine criteria of sord for example if MyArray is as:

MyArray = (5, 1, 3, 4, 2)

mean that my list need to be sort first for field5, then for field1, then for field3, then for field4, then for field2. Mine question then is: can i to do it using one only construct compare for my list?

Thanks very much for help.

Marcello Impastato
  • 2,263
  • 5
  • 30
  • 52
  • if you have a generic list, tell it to use your own compare function/method. check out: http://delphi.about.com/od/delphitips2009/qt/sort-generic.htm and http://beensoft.blogspot.com/2008/09/simple-generic-list-tlist.html for examples on how to do this. The only thing left to solve is how to make this function aware of your array that defines the sort order. No time to show you, hence a comment instead of an answer. – Marjan Venema Dec 04 '11 at 15:41
  • What about your previous question: http://stackoverflow.com/questions/8368243/list-and-binarysearch-index-not-every-correct – David Heffernan Dec 04 '11 at 17:26

3 Answers3

2

If I understand you right:

Comparer := TComparer<PMyList>.Construct(
     function(const Left, Right: PMyList): Integer
     var LV, RV, x: Integer;
     begin
        for x := Low(MyArray) to High(MyArray) do begin
           case MyArray[x] of
             1: begin
                LV := Left.Field1;
                RV := Right.Field1;
             end;
             2: begin
                LV := Left.Field2;
                RV := Right.Field2;
             end;
             ...
             else raise Exception.Create('Unhandled fileld index: '+IntToStr(MyArray[x]));
           end;
           Result := LV - RV;
           if(Result <> 0)then Break;
        end;
     end);
ain
  • 22,394
  • 3
  • 54
  • 74
2

I'm going to base the notation on your previous question. I'm also going to rename MyArray as FieldPriority.

So, FieldPriority[1] identifies the primary comparison field, FieldPriority[2] identifies the secondary comparison field and so on.

With this in place your compare function looks like this:

type
  TMyRecord  = record
    Value: array [1..5] of Integer;
  end;

function Compare(const Left, Right: TMyRecord): Integer;
var
  i, Field: Integer;
begin
  for i := 1 to 5 do
  begin
    Field := FieldPriority[i];
    Result := CompareInt(Left.Value[Field], Right.Value[Field]);
    if Result<>0 then
      exit;
  end;
end;

It all works much better if the integers in your record are declared as an array rather than individually. That allows you to index them as I do here.

Naturally this could all be generalised to handle arbitrary sized arrays.

Community
  • 1
  • 1
David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
0

A method to take field priorities into account:

OverallCompareResult := Sum(CompareResult[i] * (1 shl Priority[i]))

(1 shl Priority[i] = 2 ^ Priority[i])

Priority[] for your example: (4, 1, 3, 2, 5)

CompareResult must be in (-1, 0, 1)

MBo
  • 77,366
  • 5
  • 53
  • 86