2021년 1월 10일 일요일

리스트박스 값으로 소팅하기(ListBox, Value, Sort)

 리스트박스에 key=value 형태로 저장이 되어있을 때 Value 기준으로 소팅하고 싶으면,

문자데이터가 아니고 숫자데이터 일때

procedure TForm2.Button2Click(Sender: TObject);
var
  idx: Integer;
begin
  for idx := 0 to 30 do
    ListBox1.Items.AddPair(idx.ToString, IntToStr(Random(100)));
end;


// function TForm2.TStringsSort(List: TStringList; L, R: integer): integer;
// 이 아님에 주의!
function TStringsSort(List: TStringList; L, R: integer): integer;
begin
  Result:= StrToint(List.ValueFromIndex[L])-StrToInt(List.ValueFromIndex[R]);
end;


procedure TForm2.Button3Click(Sender: TObject);
var
  istrings: TStringList;
begin
  istrings:= TStringList.Create;
  try
    istrings.Assign(Listbox1.Items);
    istrings.CustomSort(@TStringsSort);
    Listbox1.Items.Assign(iStrings);
  finally
    istrings.Free;
  end;

2021년 1월 8일 금요일

Record를 담은 TList를 소팅하는 방법

TArray에서와 같은 방법으로 소팅하면 됩니다.

tBoll = record
    count: integer;
    name: string;
end; 
jTempArr: Tarray<tBoll>;
iList: TList<tBoll>
jBoll: tBoll;
iList := TList<TBoll>.Create;
SetLength(jTempArr, 45);
for idx := 0 to 44 do begin
      jTempArr[idx].Count := 0;
      jTempArr[idx].Name := BollName[idx];
 end;
 for idx := 0 to 44 do begin
        jBoll := jTempArr[idx];
        iList.Add(jBoll);
 end;
 iList.Sort(TComparer<TBoll>.Construct(
        function(const Left, Right: TBoll): integer begin
             Result := Left.Count - Right.Count;
             // Result:= CompareText(Left, Right);
        end));

와 같은 방법으로 하면 됩니다. 그냥 iList.sort 하였더니  숫자의 자리수가 다르거나  음수가 있으면 잘 안되더군요. 0, -99, 99 이렇게 되어있을 경우 0 이 가장적은 수가되버립니다.

잊어먹지 않기위해 정리하였습니다.  참고로 List 소팅도 정리합니다. (XE)

uses
  SysUtils, Generics.Defaults, Generics.Collections;
procedure Sort;
var
  List: TList<String>;
  AComparer: IComparer<string>;
  I: integer;
begin
  Randomize;
  AComparer:= TDelegatedComparer<string>.create(
    function(const Left, Right: String): integer begin
      Result:= StrToInt(Left) - StrToInt(Right);
    end);
  List:= TList<string>.Create(AComparer);
  for i := 0 to 9 do
    List.Add(IntToStr(Random(1000)));
  List.Sort;
  //  List.Sort(TComparer<string>.Construct(function(const left,right: string):integer begin
  //     Result:= StrToInt(Left)-StrToInt(Right); 
  //  end));
  for I := 0 to List.Count-1 do Writeln((List[i]));
  List.Free;
end;

2021년 1월 5일 화요일

레코드 형태의 행렬에서 검색하는 방법(TArray.BinarySearch)

type
  TBoll = record
    count: integer;
    name: string;
  end;
var     iArrayBoll: TArray<TBoll>;
procedure TForm1.Button2Click(Sender: TObject);
begin
  SetLength(iArrayBoll, Memo1.Lines.count);
  for var idx := low(iArrayBoll) to high(iArrayBoll) do begin
    iArrayBoll[idx].count := 25;
    iArrayBoll[idx].name := Memo1.Lines[idx];
  end;
  TArray.Sort<TBoll>(iArrayBoll, TComparer<TBoll>.Construct(
    function(const a, b: TBoll): integer begin
      // Result := StrToint(a.name) - StrToint(b.name); //숫자크기우선 정렬
      Result:= CompareText(a.name, b.name); //스트링 정상 정렬
    end));
  for var idx := low(iArrayBoll) to High(iArrayBoll) do begin
    Memo2.Lines.Add(iArrayBoll[idx].name + '=' + iArrayBoll[idx].count.ToString);
  end;
end;


procedure TForm1.Button4Click(Sender: TObject);
var
  idx: integer;
  iBoll: TBoll;
  Found: boolean;
begin
  if rbLineair.Checked then begin
    for idx := 0 to High(iArrayBoll) do
      if Edit1.Text = iArrayBoll[idx].name then begin
        Caption := intToStr(idx);
        Exit;
      end;
  end else begin
    iBoll.name := Edit1.Text;
    iBoll.count := 250;
    Found := TArray.BinarySearch<TBoll>(iArrayBoll, iBoll, idx, TDelegatedComparer<TBoll>.Construct(
      function(const a, b: TBoll): integer begin
        Result := CompareText(a.name, b.name);
      end));
    if Found then Caption := intToStr(idx) else Caption := 'No';
  end;
end;

사전에 정상 정렬되어 있지 않은 경우 아래처럼 오류가 나온다. BinarySearch 전에 스트링의 경우 정상 정렬(숫자크기우선 정렬이면 안됨)이되어 있어야 한다. 이것 때문에 두어시간 해메었었네요.

레코드 형태의 행렬을 소팅하는 방법(별거아닌데 예제가 귀해서...)

 TArray<integer>나, Tarray<string> 으로 되어 있느 배열은 Tarray.sort<integer>(iArray) 과 같은 방법으로 할수 있습니다.

procedure TForm1.Button1Click(Sender: TObject);
var     iArray: TArray<string>;
begin
  SetLength(iArray, Memo1.Lines.count);
  iArray := Memo1.Lines.ToStringArray;
  TArray.Sort<string>(iArray);
  Memo2.Lines.AddStrings(iArray);
end;

유사한 방법으로 레코드 형태의 배열이며 특정 필드로 소팅한다면

  TBoll = record
    count: integer;
    name: string;
  end;
procedure TForm1.Button2Click(Sender: TObject);
var     iArrayBoll: TArray<TBoll>;
begin
  SetLength(iArrayBoll, Memo1.Lines.count);
  for var idx := low(iArrayBoll) to high(iArrayBoll) do begin
    iArrayBoll[idx].count := 0;
    iArrayBoll[idx].name := Memo1.Lines[idx];
  end;
  TArray.Sort<TBoll>(iArrayBoll, TComparer<TBoll>.Construct(
    function(const a, b: TBoll): integer
    begin
      Result := StrToint(a.name)- StrToInt(b.name); 
     //string 의 경우에는 Result:= CompareText(a.name, b.name);
    end));
  for var idx := low(iArrayBoll) to High(iArrayBoll) do begin
    Memo2.Lines.Add(iArrayBoll[idx].name);
  end;
end;

검색은

procedure TForm1.Button3Click(Sender: TObject);

var
  iStr: string;
  idx: integer;
begin
  iStr := Edit1.Text;
  if Tarray.BinarySearch<string>(iArray, iStr, idx) then Button3.Caption:= intToStr(idx);
end;

델파이 12.1이냐 11.3이냐?

 델파이가 12.1이 나왔습니다. 혹시 11.3버전의 커뮤니티버전이 필요하시는분이 있을 수 있을 것 같아 https://altd.embarcadero.com/.../RADStudio_11_3_esd_28... 와 이것 찾느랴 엄청고생함.