2011-11-08 58 views
8

可能重複:
How can I search a generic TList for a record with a certain field value?如何搜索通用TList <T>集合?

TList<TActivityCategory> 

TActivityCategory的集合,具有字符串類型的Name屬性,我想用名稱來搜索從TList屬性。

我在TList <>中看到BinarySearch>,但這需要TActivityCategory的一個實例。我只想傳遞一個名稱的字符串。

我該怎麼做呢?

+0

我看到一個,但我不清楚。 BinarySearch正在詢問正在搜索的對象的實例。當我通過傳遞一個字符串來查找對象時,我看不出這會如何幫助。 –

+0

您必須傳遞一個自定義比較器,該比較器僅基於字符串字段執行比較。 –

+0

這就是我的想法。但是,我不知道該怎麼去做。我見過的所有示例TComparers都討論了對TList <>進行排序。沒有關於搜索。你會碰巧有一個例子嗎? –

回答

1

如果您沒有要搜索的實例,則必須進行自己的搜索。有三種基本方法可以做到這一點:

  • 二進制搜索:實現自己的二進制搜索。這隻在列表排序後纔有效。
  • 線性搜索:實現您自己的線性搜索。這將始終有效,但在大型列表中,它比二分查找要慢得多。
  • 字典查詢:保持TDictionary<string, TActivityCategory>並列表。不需要搜索,但需要編寫一些代碼來保持兩者同步。
+0

'TList '可以做到這一點與自定義比較器 –

+0

我很喜歡字典查找想法,但似乎必須有一個更優雅的解決方案。 –

+1

如果您沒有實例,則可以創建一個實例。您只需設置您在提供的比較器中實際使用的字段。 – jpfollenius

4

當您創建列表時,您可以傳入比較器。 Generics.Defaults單元中有一些比較器類,您可以傳入一些匿名方法來比較兩個元素。它們用於IndexOf,Contains或Sort等幾種方法。

例子:

uses 
    Generics.Defaults, 
    Generics.Collections; 

type 
    TActivityCategory = class 
    private 
    FName: string; 
    public 
    constructor Create(const Name: string); 
    property Name: string read FName write FName; 
    end; 

constructor TActivityCategory.Create(const Name: string); 
begin 
    FName := Name; 
end; 

procedure TForm1.Button1Click(Sender: TObject); 
var 
    activities: TList<TActivityCategory>; 
    search: TActivityCategory; 
begin 
    activities := TObjectList<TActivityCategory>.Create(
    TDelegatedComparer<TActivityCategory>.Create(
     function(const Left, Right: TActivityCategory): Integer 
     begin 
     Result := CompareText(Left.Name, Right.Name); 
     end)); 

    activities.Add(TActivityCategory.Create('Category B')); 
    activities.Add(TActivityCategory.Create('Category C')); 
    activities.Add(TActivityCategory.Create('Category A')); 

    search := TActivityCategory.Create('Category C'); 
    if activities.Contains(search) then 
    ShowMessage('found'); 

    ShowMessageFmt('Index: %d', [activities.IndexOf(search)]); 
    activities.Sort; 
    ShowMessageFmt('Index: %d', [activities.IndexOf(search)]); 


    search.Name := 'Category D'; 
    if not activities.Contains(search) then 
    ShowMessage('not found'); 

    search.Free; 
    activities.Free; 
end; 
+0

你可以傳入你的比較器,但'IComparer'要求比較的兩端必須是專用類型的實例。如果我正確地理解了它,OP就希望避免爲搜索目的創建一個實例; [這裏](http://stackoverflow.com/questions/8051327/how-can-i-search-for-single-field-in-a-generic-tlist/8055750#8055750)是我的嘗試(作爲答案到類似的問題)。 –

+0

@TOndrej:我的答案正在考慮對梅森斯的評論回答 –

1

是完全坦白,並考慮到所有的比較器爲基礎的方法所需的鍋爐板,它可能只是簡單的寫你自己的搜索程序:

type 
    TActivityCategoryList = class(TList<TActivityCategory>) 
    public 
    function Find(const Name: string): Integer; 
    end; 

function TActivityCategoryList.Find(const Name: string): Integer; 
begin 
    for Result := 0 to Count-1 do 
    if Self[Result].Name=Name then 
     exit; 
    Result := -1; 
end; 
+0

是的,當你做完某人認爲這將是一個不錯的主意,讓這個名單按類別名稱排序...... whoops –

+2

@Stefan我想我的答案是出生的從已知的解決方案將在C#中的挫折感。 Delphi和它的庫只是不可組合。比較器必須在列表創建時分配的事實是一個殺手。你如何使用這種方法按名稱和類別搜索相同的列表?有沒有我們錯過的圖書館功能? –

+2

不,這就是爲什麼每個認真使用列表並不僅僅是存儲內容的人,比如通過不同的標準進行搜索時,應該忘記內置的泛型列表並查看[Collections](http://code.google.de/)。 com/p/delphi-coll /)或[Spring](http://code.google.com/p/delphi-spring-framework/)。他們都有類似於C#中IEnumerable 擴展方法的東西。 –