Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Sort arrays by multiple fields

I have multiple arrays and they all start with integer fields, from 1 up to 5 fields, and these are like indexes that need to be sorted, from min to max:

    TArrayA = record
          Field1:integer;
          Field2:integer;
          Field3:integer;
          Field4:integer;
          Field5:integer;
          ... //other fields, strings, integers... up to 50 fields
        end;

    ArrayA:=array of TArrrayA;

Currently I use this approach to sort:

    // sort by Field1
    top:=Length(ArrayA);
      for counter := 0 to top do
        begin
          min := counter;
          for look := counter + 1 to top do
            if ArrayA[look].Field1 < ArrayA[min].Field1 then
              min := look;
          vTmpRecord := ArrayA[min];
          ArrayA[min] := ArrayA[counter];
          ArrayA[counter] := vTmpRecord;
        end;

   // now sort by Field2
    top:=Length(ArrayA);
      for counter := 0 to top do
        begin
          min := counter;
          for look := counter + 1 to top do
            if (ArrayA[look].Field1 = ArrayA[min].Field1) And 
               (ArrayA[look].Field2 < ArrayA[min].Field2) then
              min := look;
          vTmpRecord := ArrayA[min];
          ArrayA[min] := ArrayA[counter];
          ArrayA[counter] := vTmpRecord;
        end;

This does the job. Although is a bit slow when I need to sort all 5 fields, and this is how I do it, field by field, so I sort the array 5 times. Is there any better, faster way?

Here is example:

procedure TForm1.Button8Click(Sender: TObject);
type
  TArrayA = record
    Field1: integer;
    Field2: integer;
    Field3: integer;
    Field4: integer;
    Field5: integer;
  end;
var
  ArrayA: array of TArrayA;
  vTmpRecord: TArrayA;
  top, counter, min, max, look: integer;
  i,t1,t2:integer;
begin

  SetLength(ArrayA,100000);
  for i := 0 to 99999 do
  begin
    ArrayA[i].Field1:=1+Random(100);
    ArrayA[i].Field2:=1+Random(100);
    ArrayA[i].Field3:=1+Random(100);
    ArrayA[i].Field4:=1+Random(100);
    ArrayA[i].Field5:=1+Random(100);
  end;


  t1:=GetTickCount;
  // sort by Field1
  top := Length(ArrayA);
  for counter := 0 to top do
  begin
    min := counter;
    for look := counter + 1 to top do
      if ArrayA[look].Field1 < ArrayA[min].Field1 then
        min := look;
    vTmpRecord := ArrayA[min];
    ArrayA[min] := ArrayA[counter];
    ArrayA[counter] := vTmpRecord;
  end;

  // sort by Field2
  top := Length(ArrayA);
  for counter := 0 to top do
  begin
    min := counter;
    for look := counter + 1 to top do
      if (ArrayA[look].Field1 = ArrayA[min].Field1) and
        (ArrayA[look].Field2 < ArrayA[min].Field2) then
        min := look;
    vTmpRecord := ArrayA[min];
    ArrayA[min] := ArrayA[counter];
    ArrayA[counter] := vTmpRecord;
  end;

  // sort by Field3
  top := Length(ArrayA);
  for counter := 0 to top do
  begin
    min := counter;
    for look := counter + 1 to top do
      if (ArrayA[look].Field1 = ArrayA[min].Field1) and (ArrayA[look].Field2 = ArrayA[min].Field2) and
        (ArrayA[look].Field3 < ArrayA[min].Field3) then
        min := look;
    vTmpRecord := ArrayA[min];
    ArrayA[min] := ArrayA[counter];
    ArrayA[counter] := vTmpRecord;
  end;

  // sort by Field4
  top := Length(ArrayA);
  for counter := 0 to top do
  begin
    min := counter;
    for look := counter + 1 to top do
      if (ArrayA[look].Field1 = ArrayA[min].Field1) and (ArrayA[look].Field2 = ArrayA[min].Field2) and (ArrayA[look].Field3 = ArrayA[min].Field3) and
        (ArrayA[look].Field4 < ArrayA[min].Field4) then
        min := look;
    vTmpRecord := ArrayA[min];
    ArrayA[min] := ArrayA[counter];
    ArrayA[counter] := vTmpRecord;
  end;

  // sort by Field5
  top := Length(ArrayA);
  for counter := 0 to top do
  begin
    min := counter;
    for look := counter + 1 to top do
      if (ArrayA[look].Field1 = ArrayA[min].Field1) and (ArrayA[look].Field2 = ArrayA[min].Field2) and (ArrayA[look].Field3 = ArrayA[min].Field3) and (ArrayA[look].Field4 = ArrayA[min].Field4) and
        (ArrayA[look].Field5 < ArrayA[min].Field5) then
        min := look;
    vTmpRecord := ArrayA[min];
    ArrayA[min] := ArrayA[counter];
    ArrayA[counter] := vTmpRecord;
  end;

  t2:=GetTickCount;
  Button8.Caption:=IntToStr(t2-t1);
end;
like image 599
Mike Torrettinni Avatar asked Feb 11 '26 16:02

Mike Torrettinni


1 Answers

You can use built in Quick sort method for sorting arrays with your custom comparer:

uses
  System.Math,
  System.Generics.Defaults,
  System.Generics.Collections;

  TArray.Sort<TArrayA>(ArrayA, TComparer<TArrayA>.Construct( function(const Left, Right: TArrayA): Integer
  begin
    if Left.Field1 = Right.Field1 then
      begin
        if Left.Field2 = Right.Field2 then
          begin
            Result := CompareValue(Left.Field3, Right.Field3);
          end
        else Result := CompareValue(Left.Field2, Right.Field2);
      end
    else Result := CompareValue(Left.Field1, Right.Field1);
  end
  ));

I added code only for first three fields, but you will get the picture how to build your own comparer for more fields.

like image 184
Dalija Prasnikar Avatar answered Feb 15 '26 11:02

Dalija Prasnikar



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!