How to sort array of integers with zeros at the end?
Asked Answered
A

2

5

I need to sort arrays by integer field, with 1-n sorted at the beginning and zeros last: 0,0,3,1,2 -> 1,2,3,0,0

I don't know how to sort it in one go, so I tried in 2 sorts, but it doesn't produce correct results:

It does put zeros at the end, but it messes up 1-n ordered items:

0,0,3,1,2 -> (first sort) 0,0,1,2,3 -> (second sort) 2,3,1,0,0

procedure TForm2.Button1Click(Sender: TObject);
var
  Arr: TArray<integer>;
begin
  SetLength(Arr, 5);
  Arr[0] := 0;
  Arr[1] := 0;
  Arr[2] := 3;
  Arr[3] := 1;
  Arr[4] := 2;

  // First sort: Sort 1-n
  TArray.Sort<integer>(Arr, TComparer<integer>.Construct(function(const Left, Right: integer): Integer
    begin
      if Left < Right then
        Result := -1
      else if Left > Right then
        Result := 1
      else
        Result := 0;
    end
    ));

  // Second sort: Put zeros at the end
  TArray.Sort<integer>(Arr, TComparer<integer>.Construct(function(const Left, Right: integer): Integer
    begin
      if (Left = 0) and (right>0) then
        Result := 1
      else
        Result := 0;
    end
    ));
end;

Is there a way to do this kind of sort in one, single Sort operation?

Anticipant answered 6/1, 2018 at 16:33 Comment(0)
M
9

Try this, the point being to deal with the special 0 cases first in the if-then-else ladder, before the ordinary cases.

  TArray.Sort<integer>(Arr, TComparer<integer>.Construct(function(const Left, Right: integer): Integer
    begin
    if (Left = 0) and (Right = 0) then
      Result := 0
    else if (Left = 0) then
      Result := 1
    else if (Right = 0) then
      Result := -1
    else if (Left < Right) then
      Result := -1
    else if (Left > Right) then
      Result := 1
    else
      Result := 0;
    end
    ));

A brief testing shows it works OK.

Mert answered 6/1, 2018 at 20:49 Comment(1)
This works great! Now I understand why and how - as you said, first take care of 0s and then the rest.Anticipant
L
4

Just fix your compare function so that it will treat 0 as being larger than anything.

Untested:

TArray.Sort<integer>(Arr, TComparer<integer>.Construct(function(const Left, Right: integer): Integer
  begin
    if Left = Right then
      Result := 0
    else if ((Left <> 0) and (Left < Right)) or (Right = 0) then
      Result := -1
    else 
      Result := 1;
  end
  ));
Leotaleotard answered 6/1, 2018 at 16:43 Comment(3)
Thanks, but it's not working right. I tried to follow your suggestion to treat 0 as largest number, but I keep getting stuck in indefinite loop.Anticipant
The bug here is that if Left = 0 but still < Right, you incorrectly return -1;Doctor
@CraigYoung Indeed. That comes from not testing the code :) Fixed.Leotaleotard

© 2022 - 2024 — McMap. All rights reserved.