2010-07-28 33 views
6

Tengo una clase que tiene varias listas <> contenidas en ella. Es básicamente una tabla almacenada con cada columna como una lista <>. Cada columna no contiene el mismo tipo. Cada lista también tiene la misma longitud (tiene la misma cantidad de elementos).Lista de clasificación C# basada en otra lista

Por ejemplo:

tengo 3 Lista <> objetos; una lista, dos listas y tres listas.

//Not syntactically correct 
List<DateTime> one = new List...{4/12/2010, 4/9/2006, 4/13/2008}; 
List<double> two = new List...{24.5, 56.2, 47.4}; 
List<string> three = new List...{"B", "K", "Z"}; 

Quiero ser capaz de enumerar uno del más antiguo al más reciente ordenar: uno = {4/9/2006, 4/13/2008, 4/12/2010};

Para hacer esto moví el elemento 0 hasta el final.

Luego quiero ordenar la lista dos y tres de la misma manera; moviendo el primero al último.

Así que cuando ordeno una lista, quiero que los datos en el índice correspondiente en las otras listas también cambien de acuerdo con la forma en que se ordena la lista.

Supongo que tengo que sobrecargar IComparer de alguna manera, pero siento que hay un atajo del que no me he dado cuenta.

Respuesta

2

Primero debe crear un objeto de Datos para guardar todo.

private class Data 
{ 
    public DateTime DateTime { get; set; } 
    public int Int32 { get; set; } 
    public string String { get; set; } 
} 

Luego puede ordenar de esta manera.

var l = new List<Data>(); 
l.Sort(
    (a, b) => 
    { 
     var r = a.DateTime.CompareTo(b); 
     if (r == 0) 
     { 
      r = a.Int32.CompareTo(b); 
      if (r == 0) 
      { 
       r = a.String.CompareTo(b); 
      } 
     } 
     return r; 
    } 
); 
+0

Así que recorro la totalidad de cada lista y creo una estructura de datos a partir de los datos. Luego ordeno los datos, luego los vuelvo a poner en las listas. Esto tiene sentido, pero no parece tan eficiente. Al menos funcionará y es mejor de lo que tengo actualmente. Gracias. Editar: no necesariamente sé que hay 3 elementos, así que no puedo usar una estructura. Una lista probablemente podría funcionar, pero ¿tendría que preocuparme por el casting en general? –

+0

@John - ¿Recibe estas listas de una fuente incontrolable? – ChaosPandion

+0

Sí, los objetos List <> que recibo no se pueden cambiar; sin embargo, pueden ordenarse en su lugar. –

5

He manejado este diseño en el pasado manteniendo o creando una lista de índices por separado. Primero ordena la lista de índices y luego la usa para ordenar (o simplemente acceder) a las otras listas. Puede hacerlo creando un IComparer personalizado para la lista de índices. Lo que haces dentro de ese IComparer es comparar basado en índices en la lista de claves. En otras palabras, está ordenando la lista de índices indirectamente. Algo así como:

// This is the compare function for the separate *index* list. 
int Compare (object x, object y) 
{ 
    KeyList[(int) x].CompareTo(KeyList[(int) y]) 
} 

Así está ordenando la lista de índice basado en los valores de la lista de claves. Luego puede usar esa lista de claves ordenadas para reordenar las otras listas. Si no está claro, intentaré agregar un ejemplo más completo cuando me encuentre en una situación para publicar uno.

+1

+1. Este es un excelente enfoque. Tengo exactamente este tipo de "lista indirecta" en mi biblioteca personal; es útil para muchas cosas Código de muestra [aquí] (http://nitolinq.codeplex.com/SourceControl/changeset/view/57003#1161747). –

1

El uso de matrices genéricas, esto puede ser un poco engorroso.

Una alternativa es utilizar el método Array.Sort() que toma una matriz de claves y una matriz de valores para ordenar. Primero ordena la matriz de claves en orden ascendente y se asegura de que la matriz de valores se reorganice para que coincida con este orden de clasificación.

Si está dispuesto a incurrir en el costo de convertir su List<T> s en arreglos (y luego de vuelta), puede aprovechar este método.

Alternativamente, podría usar LINQ para combinar los valores de matrices múltiples en un solo tipo anónimo usando Zip(), ordenar la lista de tipos anónimos usando el campo clave, y luego dividir eso en matrices separadas.

Si desea hacer esto en su lugar, debe escribir un comparador personalizado y crear una matriz de índice separada para mantener el nuevo orden de los elementos.

3

Lamento decirlo, pero esto se siente como un mal diseño. Sobre todo porque Lista <T> no garantiza orden de los elementos antes de haber llamado una de las operaciones de clasificación (por lo que tiene un problema al insertar):

From MSDN:

la lista no se garantiza que sea ordenado Debe ordenar la Lista antes de realizar operaciones (como BinarySearch) que requieren que se ordene la Lista .

En muchos casos no tendrá problemas con esto, pero podría, y si lo hace, podría ser un error muy difícil de rastrear. Por ejemplo, creo que la implementación del marco actual de la lista <T> mantiene el orden de inserción hasta que se invoca sort, pero podría cambiar en el futuro.

Consideraría seriamente refactorizar para usar otra estructura de datos. Si aún desea implementar la ordenación basada en esta estructura de datos, crearía un objeto temporal (tal vez usando un tipo anónimo), ordene esto y vuelva a crear las listas (see this excellent answer para una explicación de cómo).

+0

Acepto que hay mejores diseños. Las principales situaciones en las que me encontré con esto en el pasado es que no tenía la propiedad de las matrices originales y que eran demasiado grandes para hacer copias separadas. Si tiene propiedad, por supuesto, rediseñe esto en una clase o estructura de IComparable con todos los datos. – TechNeilogy

+0

Realmente no puedo cambiar esto, pero aquí está la aplicación. Básicamente, los objetos List <> contienen datos y esos datos se pasan a las funciones matemáticas. Las funciones matemáticas luego pasan el resultado y se anexan a la clase de tabla. ¿Sería mejor almacenar esto en fila y proyectar las columnas con LINQ? Sin embargo, eso hace que la inserción sea mucho más complicada. –

+0

Sí, ciertamente creo que sería mejor almacenar filas. No veo cómo los insertos serían más complicados por eso; pero, una vez más, no sé sobre toda la aplicación y sus limitaciones. – driis

0

espero que esto podría ayudar:

one = one.Sort(delegate(DateTime d1, DateTime d2) 
{ 
    return Convert.ToDateTime(d2).CompareTo(Convert.ToDateTime(d1)); 
}); 
1

escribí un algoritmo de ordenación que hace esto por Nito.LINQ (aún no lanzado). Utiliza una QuickSort ingeniosa para ordenar las listas y mantiene sincronizadas todas las listas relacionadas. Source code starts here, in the IList<T>.Sort extension method.

Como alternativa, si no es una gran preocupación copiar los datos, se puede proyectar en una consulta LINQ utilizando el operador postal (requiere .NET 4.0 o Rx), para que, a continuación, tire hacia fuera cada resultado:

List<DateTime> one = ...; 
List<double> two = ...; 
List<string> three = ...; 
var combined = one.Zip(two, (first, second) => new { first, second }) 
    .Zip(three, (pair, third) => new { pair.first, pair.second, third }); 
var ordered = combined.OrderBy(x => x.first); 
var orderedOne = ordered.Select(x => x.first); 
var orderedTwo = ordered.Select(x => x.second); 
var orderedThree = ordered.Select(x => x.third); 

Naturalmente, la mejor solución es no separar los datos relacionados en primer lugar.

2

Aquí hay una manera de hacerlo usando LINQ y proyecciones. La primera consulta genera una matriz con los índices originales reordenados por los valores de fecha y hora; en su ejemplo, la matriz newOrdering tendría miembros:

{ 4/9/2006, 1 }, { 4/13/2008, 2 }, { 4/12/2010, 0 }

El segundo conjunto de declaraciones generan nuevas listas de recoger elementos mediante los índices reordenados (en otras palabras, los puntos 1, 2 y 0, en ese orden).

var newOrdering = one 
    .Select((dateTime, index) => new { dateTime, index }) 
    .OrderBy(item => item.dateTime) 
    .ToArray(); 

// now, order each list 
one = newOrdering.Select(item => one[item.index]).ToList(); 
two = newOrdering.Select(item => two[item.index]).ToList(); 
three = newOrdering.Select(item => three[item.index]).ToList(); 
+0

Me gusta esta solución, pero ¿es eficiente? No sé mucho sobre el rendimiento de la pronación; –

+0

Solo una forma de averiguarlo: :-) –

Cuestiones relacionadas