Decir que tengo un array de registros que quiero ordenar basada en uno de los campos en el registro. ¿Cuál es la mejor manera de lograr esto?

TExample = record
  SortOrder : integer;
  SomethingElse : string;
end;

var SomeVar : array of TExample;
  • Sólo fue a través de este ejercicio y encontrar la mejor manera es escribir mi propio código. No creo que ninguna de las respuestas debe ser recomendado como mejores.
  • Uno de los puntos. Tal vez se podría añadir una respuesta con la solución para el problema?
  • Hay buena información en los Tomos de Delphi Algoritmos y Estructuras de Datos por Julian Bucknall. (s
  • Hay buena información en los Tomos de Delphi Algoritmos y Estructuras de Datos. (Aquí, entre otros lugares: blog.boyet.com/blog/blog/…)
InformationsquelleAutor Marius | 2008-09-03

10 Comentarios

  1. 36

    Puede agregar punteros a los elementos de la matriz a un TList, a continuación, llamar TList.Sort con una función de comparación, y, finalmente, crear una nueva matriz y la copia de los valores de la TList en el orden deseado.

    Sin embargo, si usted está utilizando la versión siguiente, D2009, hay un nuevo colecciones de la biblioteca, que puede ordenar matrices. Se necesita un opcional IComparer<TExample> aplicación para la clasificación personalizada de los pedidos. Aquí está en acción para su caso específico:

    TArray.Sort<TExample>(SomeVar , TDelegatedComparer<TExample>.Construct(
      function(const Left, Right: TExample): Integer
      begin
        Result := TComparer<Integer>.Default.Compare(Left.SortOrder, Right.SortOrder);
      end));
    • Tenga en cuenta que en D2009 hay múltiples problemas en el compilador con los Genéricos, por lo que el uso de las colecciones de la biblioteca no es nunca una buena opción (porque de espurias compilador de errores internos y codegen de los errores). En Delphi XE esos problemas se han resuelto en gran parte, aunque el uso de los genéricos versiones implicará un impacto en el rendimiento (y puede perder algo de código claridad/debuggability).
    • Una cosa es resolver un problema. Otra cosa es escribir una solución elegante (que significa «fácil en los ojos»). No creo que en este código se ve bien o es legible suficiente. No me gustaría para Delphi a perder lentamente su legibilidad, de lo contrario habrá una razón menos para no cambiar a C# como mi idioma preferido.
    • Esta solución es compacto y conciso ya que se reutiliza un montón de frecuencia integrado de código y permite una función anónima para ser escrito en línea en lugar de forzar a crear una falsa declaración de la función sólo así puede pasar a esta función. Puede reescribir todo este código a ti mismo y tomar una página o dos o tres. O usted puede hacer en cinco líneas. Creo que es un poco discutible si la versión larga sería más «legible» que el más corto.
    • Parte del problema es que el anónimo sintaxis de la función es tan detallado. Poner toda la función en el medio de otra función es simplemente mucho menos legibles. En segundo lugar, si hemos tenido lambdas y sobre todo si nuestro clasificación se utiliza una tecla en lugar de una comparación personalizada función de la totalidad de la empresa sería una línea. Por ejemplo, en Python toda la operación de ordenación anterior sería ordenada(someVar, clave=lambda x: x.SortOrder)! Python no permitir a una función de más de una línea de lambda en línea precisamente porque los resultados en el código ilegible. Podríamos utilizar realmente las lambdas y clave tipo.
    • Gran uso de los medicamentos genéricos y el código de valores.
  2. 12

    (Sé que este es un año más tarde, pero todavía cosas útiles.)

    Skamradt sugerencia de la almohadilla de valores enteros supone que va a ordenar el uso de una cadena de comparar. Este sería lento. Formato de llamada() para cada inserción, más lento aún. En su lugar, usted quiere hacer un entero comparar.

    De empezar con un tipo de registro:

    TExample = record
      SortOrder : integer;
      SomethingElse : string;
    end;

    No indique cómo los registros se almacenan, o cómo desea acceder a ellos una vez ordenados. Así que vamos a asumir en una Matriz Dinámica:

    var MyDA  Array of TExample; 
    ...
      SetLength(MyDA,NewSize);           //allocate memory for the dynamic array
      for i:=0 to NewSize-1 do begin        //fill the array with records
        MyDA[i].SortOrder := SomeInteger;
        MyDA[i].SomethingElse := SomeString;
      end;

    Ahora desea ordenar a esta matriz el valor entero de Orden. Si lo que quiere es un TStringList (así que usted puede utilizar el ts.Método de búsqueda), entonces usted debe agregar cada cuerda de la lista y añadir la Orden como un puntero. A continuación, ordenar en el puntero:

    var  tsExamples: TStringList;         //declare it somewhere (global or local)
    ...
      tsExamples := tStringList.create;   //allocate it somewhere (and free it later!)
    ...
      tsExamples.Clear;                   //now let's use it
      tsExamples.sorted := False;         //don't want to sort after every add
      tsExamples.Capacity := High(MyDA)+1 //don't want to increase size with every add
                                          //an empty dynamic array has High() = -1
      for i:=0 to High(MyDA) do begin
        tsExamples.AddObject(MyDA[i].SomethingElse,TObject(MyDA[i].SortOrder));
      end;

    Nota el truco de convertir el Entero Orden en un TObject puntero, que se almacena en el TStringList.De la propiedad del objeto. (Esto depende del hecho de que un número Entero y Puntero son del mismo tamaño.) En algún lugar, debemos definir una función para comparar la TObject punteros:

    function CompareObjects(ts:tStringList; Item1,Item2: integer): Integer;
    var i,j: integer;
    begin
      Result := integer(ts.Objects[i]) - integer(ts.Objects[j];
    end;

    Ahora, podemos ordenar los tsList en .Objeto llamando .CustomSort en lugar de .Sort (que ordenaría en la cadena de valor.)

    tsExample.CustomSort(@CompareObjects);     //Sort the list

    La TStringList es ahora ordenados, por lo que se puede iterar sobre ella desde 0 .Count-1 y leer las cadenas en orden.

    Pero supongamos que usted no quiere un TStringList, sólo una matriz en orden. O los registros contienen más datos de los que sólo una cadena en este ejemplo, y su orden es más complejo. Usted puede saltar el paso de la adición de cada cadena, y sólo tiene que añadir el índice de la matriz como Elementos en un TList. Hacer todo lo anterior de la misma manera, excepto que usar un TList en lugar de TStringList:

    var Mlist: TList;                 //a list of Pointers
    ...
      for i:=0 to High(MyDA) do
        Mlist.add(Pointer(i));        //cast the array index as a Pointer
      Mlist.Sort(@CompareRecords);    //using the compare function below
    
    function CompareRecords(Item1, Item2: Integer): Integer;
    var i,j: integer;
    begin
      i := integer(item1);            //recover the index into MyDA
      j := integer(item2);            // and use it to access any field
      Result := SomeFunctionOf(MyDA[i].SomeField) - SomeFunctionOf(MyDA[j].SomeField);
    end;

    Ahora que Mlist se ordena, lo utilizan como una tabla de búsqueda para acceder a la matriz en orden:

      for i:=0 to Mlist.Count-1 do begin
        Something := MyDA[integer(Mlist[i])].SomeField;
      end;

    Como me recorre el TList, regresar de nuevo a la matriz de índices ordenados. Sólo tenemos que echarlos de nuevo a los números enteros, ya que el TList piensa que son punteros.

    Me gusta hacerlo de esta manera, pero también podría poner real punteros a elementos de un array en el TList añadiendo la Dirección del elemento de la matriz en lugar de su índice. A continuación, el uso de ellos se proyectaría como punteros a TExample registros. Esto es lo que Barry Kelly y CoolMagic dijo en sus respuestas.

    • parece que es sólo un día más tarde 🙂
  3. 3

    Si su necesidad ordenados por cadena, a continuación, utilizar ordenados TStringList y
    agregar registro por TString.AddObject(string, Pointer(int_val)).

    Pero Si necesita ordenar por campo de entero y de cadena de uso de la TObjectList y después de agregar todos los registros de llamadas TObjectList.Sort necesarios ordenados funciones como parámetro.

  4. 2

    Todo depende del número de registros que se va a ordenar. Si son solo de clasificación menos de un par de cientos, a continuación, el otro tipo de métodos de trabajo bien, si usted va a ser la clasificación más, entonces tome una buena mirada en el viejo fiel Turbo Power SysTools proyecto. Hay un muy buen algoritmo de ordenación incluidos en la fuente. Uno que hace un muy buen trabajo de clasificación de millones de registros en una forma eficiente.

    Si usted va a utilizar el tStringList método de ordenación de una lista de registros, asegúrese de que su entero se rellena a la derecha antes de insertarlo en la lista. Usted puede utilizar el format(‘%.10d’,[rec.orden]) a la derecha de alinear a 10 dígitos, por ejemplo.

  5. 1

    El algoritmo quicksort se utiliza a menudo cuando el ayuno se requiere la ordenación. Delphi es (O era) utilizando la Lista.Ordenar por ejemplo.
    La Lista Delphi puede ser usado para ordenar cualquier cosa, pero es un peso pesado contenedor, que se supone que se vea como un array de punteros a estructuras. Es pesado, incluso si vamos a utilizar trucos como el Tipo de Gordon en este hilo (que pone índice o cualquier cosa en lugar de punteros, o poner directamente los valores si son más pequeños que los de 32 bits): necesitamos construir una lista y así sucesivamente…

    En consecuencia, una alternativa fácil y rápido ordenar una matriz de estructura podría ser la utilización de qsort C tiempo de ejecución de la función de msvcrt.dll.

    Aquí es una declaración que puede ser bueno (Advertencia: el código de la portátil en windows solamente).

    type TComparatorFunction = function(lpItem1: Pointer; lpItem2: Pointer): Integer; cdecl;
    procedure qsort(base: Pointer; num: Cardinal; size: Cardinal; lpComparatorFunction: TComparatorFunction) cdecl; external 'msvcrt.dll';

    Ejemplo completo aquí.

    Aviso que directamente la clasificación de la matriz de registros puede ser lento si los registros son grandes. En ese caso, ordenar una matriz de puntero a los registros puede ser más rápido (de alguna manera, como enfoque de Lista).

  6. 1

    Con una matriz, me gustaría utilizar quicksort o posiblemente heapsort, y acaba de cambiar la comparación con el uso de TExample.SortOrder, el swap parte es todavía va a actuar en la matriz y de intercambio de punteros. Si la matriz es muy grande, entonces usted puede desear una lista vinculada de la estructura si hay un montón de inserción y eliminación.

    C basados en rutinas, hay varios aquí
    http://www.yendor.com/programming/sort/

    Otro sitio, pero ha pascal fuente
    http://www.dcc.uchile.cl/~rbaeza/handbook/sort_a.html

  7. 0

    Uso uno de la especie alorithms proponer por Wikipedia. La función de Intercambio swap debería elementos de la matriz de uso temporal de una variable del mismo tipo que los elementos de la matriz. El uso de una ordenación estable si desea que las entradas con el mismo Orden entero de valor para permanecer en el orden en que estaban en el primer lugar.

  8. 0

    TStringList han eficiente Método de Ordenación.

    Si desea Ordenar el uso de un TStringList objeto con Sorted propiedad en True.

    NOTA: Para obtener más velocidad, añadir objetos en una no se Ordenan TStringList y al final el cambio de la propiedad en True.

    NOTA: Para ordenar por Campo entero, convertir a String.

    NOTA: Si hay valores duplicados, este método no es Válido.

    Se refiere.

  9. 0

    Si usted tiene Delphi XE2 o posterior, usted puede probar:

    var 
      someVar: array of TExample;
      list: TList<TExample>;
      sortedVar: array of TExample;
    begin
      list := TList<TExample>.Create(someVar);
      try
        list.Sort;
        sortedVar := list.ToArray;
      finally
        list.Free;
      end;
    end;
  10. -1

    He creado un ejemplo muy simple que funciona correctamente si el tipo de campo es una cadena.

    Type
      THuman = Class
      Public
        Name: String;
        Age: Byte;
        Constructor Create(Name: String; Age: Integer);
      End;
    
    Constructor THuman.Create(Name: String; Age: Integer);
    Begin
      Self.Name:= Name;
      Self.Age:= Age;
    End;
    
    Procedure Test();
    Var
     Human: THuman;
     Humans: Array Of THuman;
     List: TStringList;
    Begin
    
     SetLength(Humans, 3);
     Humans[0]:= THuman.Create('David', 41);
     Humans[1]:= THuman.Create('Brian', 50);
     Humans[2]:= THuman.Create('Alex', 20);
    
     List:= TStringList.Create;
     List.AddObject(Humans[0].Name, TObject(Humans[0]));
     List.AddObject(Humans[1].Name, TObject(Humans[1]));
     List.AddObject(Humans[2].Name, TObject(Humans[2]));
     List.Sort;
    
     Human:= THuman(List.Objects[0]);
     Showmessage('The first person on the list is the human ' + Human.name + '!');
    
     List.Free;
    End;

Dejar respuesta

Please enter your comment!
Please enter your name here