2011-11-08 9 views
8

Duplicar posible:
How can I search a generic TList for a record with a certain field value?¿Cómo busco una colección genérica de TList <T>?

tengo una colección de

TList<TActivityCategory> 

TActivityCategory tiene una propiedad Name de tipo cadena y quiero buscar el TList usando el nombre propiedad.

Veo BinarySearch en el TList <> pero eso requeriría una instancia de TActivityCategory. Solo quiero pasar la cadena por un nombre.

¿Cómo voy a hacer esto?

+0

I lo vi pero no está claro para mí. BinarySearch está solicitando una instancia del objeto que se busca. No veo cómo esto puede ayudar cuando estoy buscando el objeto pasando una cadena. –

+0

Tiene que pasar un comparador personalizado, que realiza la comparación basada únicamente en el campo de cadena. –

+0

Eso es lo que estaba pensando. Pero, no estoy seguro de cómo hacerlo. Todos los TComparers de muestra que he visto hablan sobre la clasificación de TList <>. Ninguno sobre buscar ¿Tendría un ejemplo? –

Respuesta

1

Si no tiene una instancia para buscar, debe hacer su propia búsqueda. Hay tres formas básicas de hacer esto:

  • búsqueda binaria: implementar su propia búsqueda binaria. Esto solo funcionará si la lista está ordenada.
  • Búsqueda lineal: Implemente su propia búsqueda lineal. Esto siempre funcionará, pero en listas grandes es significativamente más lento que una búsqueda binaria.
  • Buscar en el diccionario: Mantener un TDictionary<string, TActivityCategory> junto a la lista. No es necesario realizar búsquedas, aunque debe escribir un código para mantener las dos sincronizadas.
+0

'TList ' puede hacer esto bien con un comparador personalizado –

+0

Me gusta la idea de búsqueda del diccionario, pero parece que debe haber una solución más elegante. –

+1

Si no tiene una instancia, puede simplemente crear una. Solo tiene que establecer los campos que realmente utiliza en su comparador proporcionado. – jpfollenius

4

Al crear la lista, puede pasarla a un comparador. Hay algunas clases de comparación en la unidad Generics.Defaults donde puede pasar un método anónimo para comparar dos elementos. Se usan para varios métodos, como IndexOf, Contains u Sort.

Ejemplo:

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

Puede pasar su comparer pero 'IComparer' requiere que ambos lados de la comparación sean instancias del tipo especializado. Si lo entendí correctamente, el OP quiere evitar crear una instancia con el propósito de buscar; [aquí] (http://stackoverflow.com/questions/8051327/how-can-i-search-for-single-field-in-a-generic-tlist/8055750#8055750) es mi intento (como respuesta a una pregunta similar). –

+0

@TOndrej: Mi respuesta es considerar los comentarios de Masons answer –

1

Para ser perfectamente franco, y teniendo en cuenta toda la placa de la caldera requerido para un enfoque basado en el comparador, que sólo puede ser más simple para escribir su propia rutina de búsqueda:

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

Sí, y cuando termines alguien piensa que sería una buena idea tener esa lista ordenada por nombre de categoría ... whoops –

+2

@Stefan Supongo que mi respuesta ha nacido de la frustración de conocer cuál sería la solución en C#. Delphi y su biblioteca simplemente no son lo suficientemente compostables. El hecho de que el comparador tenga que asignarse en el momento de creación de la lista es un asesino. ¿Cómo se usa ese enfoque para buscar en la misma lista por nombre y categoría? ¿Hay alguna función de biblioteca que hayamos perdido? –

+2

No, y esa es la razón por la que todo el mundo que está trabajando seriamente con listas y haciendo algo más que almacenar cosas como buscar por diferentes criterios debe olvidarse de las listas genéricas integradas y mirar [Colecciones] (http://code.google. com/p/delphi-coll /) o [Spring] (http://code.google.com/p/delphi-spring-framework/). Ambos tienen cosas similares a los métodos de extensión IEnumerable en C#. –

Cuestiones relacionadas