Delphi如何搜索类型记录数组的索引

问题描述 投票:0回答:2

是否有更好的方法来查找类型化数组的索引? 是否可以将每个记录存储在Tlist或其他内容中,然后找到索引。

例如,我喜欢有index=FindIndexof(12345)和返回游戏数组索引的函数。 目前我正在使用以下代码,但我认为是错误的,因为我将event_id存储在内存中的两个位置。

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    procedure FormCreate(Sender: TObject);
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

  TMyEvent = record
    Event_id: longint;
    Myarray: array[0..2] of Integer;
    MyString: string;
  end;

const max_events=100;

var
  Form1: TForm1;
  MyEvents:Array[0..max_events] of TMyEvent;
  MyListIndex:  TStringlist;

implementation

{$R *.DFM}

procedure TForm1.FormCreate(Sender: TObject);
var x:Integer;
begin
  randomize;
  MyListIndex:=TStringlist.create;
  for x:=0 to max_events do
  begin
    with myEvents[x] do
    begin
      Event_id:=Random(10000)+1;
      Myarray[0]:=1;
      Myarray[1]:=2;
      MyListIndex.add('^'+formatfloat('0',Event_id)+'^');
    end;
  end;
end;

procedure TForm1.Button1Click(Sender: TObject);
var MyIndexId:Longint;
begin
  MyIndexId:=MyListIndex.indexof('^12345^');
  // and after I can process
  // myEvents[MyIndexId].Event_id
end;

end.
delphi
2个回答
2
投票

这取决于。

如果您经常进行这些查找,则需要先对数组进行排序,然后再进行二进制搜索。如果您一直插入项目并且不经常进行查找,那么您只需要保留原样。

以下是如何对项目进行排序,首先将记录放入数组中。

//Pull in TArray.Sort and TArray.BinarySearch
uses System.Generics.Collections;


Events: TArray<TMyEvent>;
....
SetLength(Events, MyEventCount); 
for i:= 0 to MyEventCount-1 do begin
  //Read in events
end;

现在使用它们排序

procedure SortEvents(var Events: TArray<TMyEvent>);
begin
  TArray.Sort<TMyEvent>(Events, TDelegatedComparer<TMyEvent>.Construct(
    function(const Left, Right: TMyEvent): Integer
    begin
      if Left.EventId > Right.EventId then Exit(1);
      if Left.EventId < Right.EventId then Exit(-1);
      Result:= 0;  //or raise an error if duplicates are not allowed.
    end
  ));
end;

见:TArray.Sort<T>

如果你想搜索做:

function EventByIndex(const Events: TArray<TMyEvent>; EventId: longint; out Index: integer): TMyEvent;
var
  Dummy: TMyEvent;
  Found: boolean;
begin
  Dummy.EventId:= EventId;
  Found:= TArray.BinarySearch(Events,  Dummy, Index, 
    TDelegatedComparer<TMyEvent>.Construct(
    function(const Left, Right: TMyEvent): Integer
    begin
      if Left.EventId > Right.EventId then Exit(1);
      if Left.EventId < Right.EventId then Exit(-1);
      Result:= 0;  //or raise an error if duplicates are not allowed.
    end
  ));
  if Found then Result:= Events[Index]
  else Index:= -1;
end; 

见:TArray.BinarySearch<T>

请注意,如果数组按排序顺序,则只能执行BinarySearch。 如果EventID不是唯一的,那么这个函数只返回一个结果,但当然EventId相同的项目就在返回的项目旁边,所以你应该能够从那里开始工作。

如果你只想做线性搜索,请执行以下操作:

function EventIndexOf(const Events: TArray<MyEvent>; EventId: longint): integer;
var
  i: integer;
begin
  for i:= 0 to High(Events) do if Events[i].EventId = EventId then Exit(i);
end;

备注 显然,不需要存储重复数据。将数字存储在Int(或Int64(如果它们很大)中),将文本存储在字符串中。 请不要滥用TStringList来存储记录数据。 TList<TSomeRecord>TArray<TSomeRecord>更适合这个目的。

全局变量很糟糕,试着永远不要写这样的代码:

unit X;
interface
...
var
  Form1: TForm1;
  MyEvents:Array[0..max_events] of TMyEvent;
  MyListIndex:  TStringlist;
implementation ....

把我们自己的变量放在TForm1的私有部分(或任何适合你目的的类)。


0
投票

如果你使用TList<T>而不是TArray<T>那么你的生活会变得更容易。如果你将IComparer<T>传递给你的TList<T>构造函数,那么SortIndexOf方法可以用它来排序/查找项目。我展示了匿名和类方法IComparer<TMyRec>用法的示例。你可以注释掉你不喜欢的那个。

intrface

type
  TMyRec = packed record
    a, b : integer;
  end;

  TForm1 = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
    function compareRecs( const left_, right_ : TMyRec ) : integer;
  public
    { Public declarations }
  end;

implementation

uses
  Generics.Collections, Generics.Defaults;

function TForm1.compareRecs( const left_, right_ : TMyRec ) : integer;
begin
  result := left_.a - right_.a;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  iC : IComparer<TMyRec>;
  aL : TList<TMyRec>;
  aMR : TMyRec;
  i : integer;
begin
  iC := TComparer<TMyRec>.Construct( function ( const left_, right_ : TMyRec ) : integer begin result := left_.a - right_.a; end );
  //iC := TComparer<TMyRec>.Construct( compareRecs );
  aL := TList<TMyRec>.create( iC );
  try
    for i := 1 to 5 do
    begin
      aMR.a := 6-i;
      aMR.b := i;
      aL.Add( aMR );
    end;
    // The order of the items is in reverse order (by TMyRec.a)
    aL.Sort;
    // The order of the items is in the right order (by TMyRec.a)
    aMR.a := 3;
    i := aL.indexOf( aMR );
    // i = 2
  finally
    aL.Free;
  end;
end;

当您通过记录调用indexOf方法时,结果索引仅取决于比较器函数中使用的记录值。在这种情况下TMyRec.a。要使用通用列表(TList<T>),您必须使用Generics.Collections单元。如果要使用自定义排序,请使用IComparer<T>实现。为此,您必须使用`Generics.Defaults'单元。

© www.soinside.com 2019 - 2024. All rights reserved.