Search code examples
delphigenericssortingtobjectlist

Sorting TObjectList<T> issue within same-comparator-value clusters


I want to sort TObjectList<T> using my own comparer function the comparer function code shown below.

I want from my ObjectList to be able to sort in two direction ascending and descending in order to accomplish this I use SysUtil.CompareText which has two parameters S1 and S2 and to sort in descending way I just opposite the sign of the CompareText result. I don't know if exist another solution everything is fine if s1 greater than S2 or vice versa however if s1 = s2 in the normal case there is no reindex in the list because all elements in the column are identical but the Opposite happened TObjectList sorted the list as if s1 > s2 or s1 < s2..

My question is how implement a comparer that supports equality and differences?

TPerson = class
private
  FName: string;
  FId: string;
public
  property Name: string read FName write FName;
  property ID: string read FID write FID;
end;

TPersons = class(TObjectList<TPerson>)
public
  constructor Create();
  procedure Sort(Direction: string); reintroduce;
end;

procedure TForm4.Button1Click(Sender: TObject);
var
  PersonsList: TPersons;
  I: Integer;
begin
  PersonsList := TPersons.Create;
  PersonsList.Sort('Ascending');
  for I := 0 to PersonsList.Count - 1 do
    ShowMessage(PersonsList[i].Name);
end;

{ TPersons }

constructor TPersons.Create;
var
  Person: TPerson;
begin
  Person := TPerson.Create;
  Person.Name := 'fateh';
  Person.ID := '1';
  Self.Add(Person);

  Person := TPerson.Create;
  Person.Name := 'mohamed';
  Person.ID := '1';
  Self.Add(Person);

  Person := TPerson.Create;
  Person.Name := 'oussama';
  Person.ID := '1';
  Self.Add(Person);
  // all ids are identical
end;

procedure TPersons.Sort(Direction: string);
var
  Comparer   : IComparer<TPerson>;
  Comparison : TComparison<TPerson>;
begin
  if Direction = 'Ascending' then

    Comparison := function(const Person1, Person2 : TPerson): Integer
                  begin
                    result := CompareText(Person1.ID, Person2.ID);
                  end;

  if Direction = 'Descending' then

    Comparison := function(const Person1, Person2 : TPerson): Integer
                  begin
                    result := - CompareText(Person1.ID, Person2.ID);
                  end;

    Comparer := TComparer<TPerson>.Construct(Comparison);

    inherited Sort(Comparer);
end;

Solution

  • if s1 = s2 in the normal case there is no reindex in the list because all elements in the column are identical

    That is wrong idea. Computer should do what you told it - but nothing more. If you told computer that those objects are equal (in other word, comparator function returned zero) - then the computer is in his right to put them in ANY ORDER that suits his internal sorting implementation.

    If you do want some SPECIFIC order among clusters of objects with the same ID - that just means that objects with same ID are NOT REALLY EQUAL, at least not all of those.

    Sure, if IDs differ - then objects do as well. But even if those IDs are the same - as long as you care about ordering, that just proves that those objects are still DIFFERENT to You and ID equality is just NOT ENOUGH to specify objects totally equal. And that means You should use nesting, cascading comparator, using more and more finegrain tests until difference spotted.

    Comparison := function(const Person1, Person2 : TPerson): Integer
      begin
         Result := CompareText(Person1.ID, Person2.ID);
         if Result <> 0 then exit;
    
         Result := CompareText(Person1.Name, Person2.Name);
         if Result <> 0 then exit;
    
         Result := Person1.Age - Person2.Age;
         if Result <> 0 then exit;
    
         Result := GenderCompare(Person1.Sex, Person2.Sex);
         if Result <> 0 then exit;
    
         Result := Person1.Salary - Person2.Salary;
         if Result <> 0 then exit;
    
         ...et cetera
      end;
    

    Comparing is a kind of mathematics, kind of Algebra. You prove some axioms and then some theories start to work. But only after axioms proving and not more than actual theorem terms.

    The very fact that you care about order of objects with the same ID shows that the axiom is false. The comparison is more complex process than ID alone. You have to make a comparator that would only return zero on truly equal o\bjects, in other words the objects about which respective order you don't care absolutely.

    Try reading http://www.howzatt.demon.co.uk/articles/2011-05-equality.html