Nomi Laura
Nomi Laura

Reputation: 23

Delphi String Sort

I have a file with very big num like below

45313904626416486480179546360796323469287116537171
465573254230695450538671922463236910370073247307526
5906233480284069039032926795367974774430427486375

How to sort this kind of num ?

The result should be something like (real file is 100000 lines):

5906233480284069039032926795367974774430427486375
45313904626416486480179546360796323469287116537171
465573254230695450538671922463236910370073247307526

I try something with

  MyFlexibleArray := TList<UInt64>.Create;
  AssignFile(F, OpenTextFileDialog1.FileName);
  Reset(F);
  repeat
    Readln(F, str);
    MyFlexibleArray.Add(UInt64(str));
  until EOF(F);
  CloseFile(F);
  MyFlexibleArray.Sort;

With a TStringList, the result wasn't sort in natural way!

Any help will be greatly appreciated.

full file

Upvotes: 1

Views: 1080

Answers (3)

Rob Lambden
Rob Lambden

Reputation: 2293

If you sort your data as a string, the length of the string is not being taken into account.

If you use a Generic (so you will need System.Generics.Collections in your uses clause) you can specify how to compare objects in a parameter to the constructor. This means that your list of strings would be declared as:

  FMyStrings: TList<String>;

You comparator would compare two strings, if you assume that the strings can only ever contain decimal digits then your comparator would be something like:

  TMyStringSorter = class(TComparer<String>)
  public
    function Compare(const Left, Right: String): Integer; override;
  end;

  function TMyStringSorter.Compare(const Left, Right: String): Integer;
  begin
    if(Length(Left)<Length(Right) then Result:=-1
    else if(Length(Right)<Length(Left) then Result:=1
    else Result:=CompareStr(Left, Right);
  end;

Then pass the Interface to the comparer to the TList Constructor and you can sort it according to your own sort algorithm.

Upvotes: 2

Matthias B
Matthias B

Reputation: 1025

You could keep the big numbers in strings in a TStringList, and sort it using a custom comparer that first sorts by string length and then (if length is equal) by string value. Like this:

function NumberStringComparer(List: TStringList; Index1, Index2: Integer): Integer;
var
  Value1, Value2: string;
  Len1, Len2: Integer;
begin
  Value1 := List[Index1];
  Value2 := List[Index2];
  Len1 := Length(Value1);
  Len2 := Length(Value2);

  Result := Len1 - Len2;
  if Result = 0 then
  begin
    if Value1 = Value2 then
      Result := 0
    else if Value1 > Value2 then
      Result := 1
    else
      Result := -1;
  end;
end;

Usage example:

MyStringList.CustomSort(NumberStringComparer);

Upvotes: 0

Andreas Rejbrand
Andreas Rejbrand

Reputation: 108948

First, your text file is corrupt. It contains NUL bytes, which makes it impossible to parse it normally.

However, if we disregard this issue, sorting a file like this is almost trivial.

Assuming there are no leading zeros, the following algorithm will give the correct result:

var Data := TFile.ReadAllLines('K:\numbers.txt', TEncoding.ASCII);

TArray.Sort<string>(
  Data,
  TComparer<string>.Construct(
    function(const L, R: string): Integer
    begin
      Result := CompareValue(L.Length, R.Length);
      if Result <> 0 then
        Exit;
      for var i := 1 to L.Length do
      begin
        Result := CompareValue(Ord(L[i]), Ord(R[i]));
        if Result <> 0 then
          Exit;
      end;
    end
  )
);

TFile.WriteAllLines('K:\sorted.txt', Data, TEncoding.ASCII);

We construct our own string comparer according to these rules:

  • If L has more (fewer) digits than R, then clearly it is greater (smaller).
  • If L and R has the same number of digits, compare the digits, one by one, from the MSD to the LSD.

Just add IOUtils, Generics.Defaults, Generics.Collections, and Math to your uses clause.

Upvotes: 4

Related Questions