2010-08-05 21 views
12

Tengo un conjunto de elementos/claves que estoy leyendo de dos archivos de configuración diferentes. Entonces las claves pueden ser iguales pero con diferentes valores asociados con cada una de ellas.¿Cómo ordenar la lista con claves duplicadas?

Quiero enumerarlos en el orden ordenado. Que puedo hacer ? Intenté con la clase SortedList, pero no permite las claves duplicadas.

¿Cómo puedo hacerlo?

por ejemplo digamos que tengo 3 elementos con las teclas 1,2,3. Entonces obtengo un elemento más que tiene la clave 2 (pero diferente valor). Luego quiero que la nueva clave se inserte después de la clave existente 2 pero antes 3. Si encuentro un elemento con la clave 2, entonces debería ir después de la última clave agregada 2.

Tenga en cuenta que estoy usando. NET 2.0

+0

¿Realmente te importa si los elementos con las mismas llaves van antes o después de los elementos existentes? – BlueMonkMN

+0

Sí. Quiero mantener el orden como se menciona en mi pregunta – Learner

Respuesta

12

yo prefiero usar LINQ para este tipo de cosas:

using System.Linq; 

... 

var mySortedList = myList.Orderby(l => l.Key) 
         .ThenBy(l => l.Value); 

foreach (var sortedItem in mySortedList) { 
    //You'd see each item in the order you specified in the loop here. 
} 

Nota: debe utilizar .NET 3.5 o posterior para lograr esto.

+0

Gracias pero estoy usando .NET 2.0 – Learner

+0

Yuck. Esto solo es razón suficiente para actualizarse. –

+1

esto no es posible cuando se utiliza .net 2.0, por lo que no es una respuesta a su pregunta – Nealv

1

.NET no tiene un gran soporte para géneros estables (lo que significa que los elementos equivalentes mantienen su orden relativo cuando se ordenan). Sin embargo, puede escribir su propia inserción ordenada estable usando List.BinarySearch y un IComparer<T> personalizado (que devuelve -1 si la clave es menor que o igual a el objetivo, y +1 si es mayor).

Tenga en cuenta que List.Sort no es un tipo estable, por lo que tendrá que escribir su propia rutina de conexión rápida estable o simplemente utilizar la ordenación por inserción para poblar inicialmente la colección.

9

lo que necesita es una función Ordenar con un IComparer personalizado. Lo que tienes ahora es el icomparer predeterminado cuando usas sort. esto verificará el valor de un campo.

Cuando crea un IComparer personalizado (esto lo hace en su clase implementando la interfaz Icomparable). lo que hace es: su objeto se controla a sí mismo a todos los demás objetos de la lista que ordena.

esto se hace por una función. (no se preocupe VS lo implementará cuando refiera su interfaz

public class ThisObjectCLass : IComparable{ 

    public int CompareTo(object obj) { 
      ThisObjectCLass something = obj as ThisObjectCLass ; 
      if (something!= null) 
       if(this.key.CompareTo(object.key) == 0){ 
       //then: 
        if ..... 
       } 
       else if(this.value "is more important then(use some logic here)" something.value){ 
       return 1 
       } 
       else return -1 
      else 
       throw new ArgumentException("I am a dumb little rabid, trying to compare different base classes"); 
     } 
} 

Lea en los enlaces de arriba para obtener una mejor información.

Sé que tenía la comprensión de esto mismo en el principio con algunas dificultades, por lo que para cualquier ayuda adicional añadir un comentario y voy a elaborar

2

Si realmente no se preocupan acerca de la secuencia de los elementos con claves iguales, añadir todo a una lista y luego ordenarla por clave:

static void Main(string[] args) 
{ 
    List<KeyValuePair<int, MyClass>> sortedList = 
     new List<KeyValuePair<int, MyClass>>() { 
     new KeyValuePair<int, MyClass>(4, new MyClass("four")), 
     new KeyValuePair<int, MyClass>(7, new MyClass("seven")), 
     new KeyValuePair<int, MyClass>(5, new MyClass("five")), 
     new KeyValuePair<int, MyClass>(4, new MyClass("four-b")), 
     new KeyValuePair<int, MyClass>(7, new MyClass("seven-b")) 
     }; 
    sortedList.Sort(Compare); 
} 
static int Compare(KeyValuePair<int, MyClass> a, KeyValuePair<int, MyClass> b) 
{ 
    return a.Key.CompareTo(b.Key); 
} 

Si realmente desea que los elementos insertados después de ser insertado después de los anteriores, ordenarlas, ya que se insertan:

class Sorter : IComparer<KeyValuePair<int, MyClass>> 
{ 

static void Main(string[] args) 
{ 
    List<KeyValuePair<int, MyClass>> sortedList = new List<KeyValuePair<int, MyClass>>(); 
    Sorter sorter = new Sorter(); 
    foreach (KeyValuePair<int, MyClass> kv in new KeyValuePair<int, MyClass>[] { 
     new KeyValuePair<int, MyClass>(4, new MyClass("four")), 
     new KeyValuePair<int, MyClass>(7, new MyClass("seven")), 
     new KeyValuePair<int, MyClass>(5, new MyClass("five")), 
     new KeyValuePair<int, MyClass>(4, new MyClass("four-b")), 
     new KeyValuePair<int, MyClass>(4, new MyClass("four-c")), 
     new KeyValuePair<int, MyClass>(7, new MyClass("seven-b")) }) 
    { 
     sorter.Insert(sortedList, kv); 
    } 
    for (int i = 0; i < sortedList.Count; i++) 
    { 
     Console.WriteLine(sortedList[i].ToString()); 
    } 
} 
void Insert(List<KeyValuePair<int, MyClass>> sortedList, KeyValuePair<int, MyClass> newItem) 
{ 
    int newIndex = sortedList.BinarySearch(newItem, this); 
    if (newIndex < 0) 
     sortedList.Insert(~newIndex, newItem); 
    else 
    { 
     while (newIndex < sortedList.Count && (sortedList[newIndex].Key == newItem.Key)) 
     newIndex++; 
     sortedList.Insert(newIndex, newItem); 
    } 
} 
#region IComparer<KeyValuePair<int,MyClass>> Members 

public int Compare(KeyValuePair<int, MyClass> x, KeyValuePair<int, MyClass> y) 
{ 
    return x.Key.CompareTo(y.Key); 
} 

#endregion 
} 

O usted podría tener una lista ordenada de las listas:

static void Main(string[] args) 
{ 
    SortedDictionary<int, List<MyClass>> sortedList = new SortedDictionary<int,List<MyClass>>(); 
    foreach (KeyValuePair<int, MyClass> kv in new KeyValuePair<int, MyClass>[] { 
     new KeyValuePair<int, MyClass>(4, new MyClass("four")), 
     new KeyValuePair<int, MyClass>(7, new MyClass("seven")), 
     new KeyValuePair<int, MyClass>(5, new MyClass("five")), 
     new KeyValuePair<int, MyClass>(4, new MyClass("four-b")), 
     new KeyValuePair<int, MyClass>(4, new MyClass("four-c")), 
     new KeyValuePair<int, MyClass>(7, new MyClass("seven-b")) }) 
    { 
     List<MyClass> bucket; 
     if (!sortedList.TryGetValue(kv.Key, out bucket)) 
     sortedList[kv.Key] = bucket = new List<MyClass>(); 
     bucket.Add(kv.Value); 
    } 
    foreach(KeyValuePair<int, List<MyClass>> kv in sortedList) 
    { 
     for (int i = 0; i < kv.Value.Count; i++) 
     Console.WriteLine(kv.Value[i].ToString()); 
    } 
} 

No estoy seguro de si se puede usar inicializadores Lista de .NET 2.0 como lo hice en el primer ejemplo anterior, pero estoy seguro de que sabes cómo llenar una lista con datos.

0

¿Contempló la clase NameValueCollection, ya que le permite almacenar varios valores por clave? Por ejemplo, podría tener lo siguiente:

NameValueCollection nvc = new NameValueCollection(); 
    nvc.Add("1", "one"); 
    nvc.Add("2", "two"); 
    nvc.Add("3", "three"); 

    nvc.Add("2", "another value for two"); 
    nvc.Add("1", "one bis"); 

y luego para recuperar los valores que podría tener:

for (int i = 0; i < nvc.Count; i++) 
    { 
     if (nvc.GetValues(i).Length > 1) 
     { 
      for (int x = 0; x < nvc.GetValues(i).Length; x++) 
      { 
       Console.WriteLine("'{0}' = '{1}'", nvc.GetKey(i), nvc.GetValues(i).GetValue(x)); 
      } 
     } 
     else 
     { 
      Console.WriteLine("'{0}' = '{1}'", nvc.GetKey(i), nvc.GetValues(i)[0]); 
     } 

    } 

que dan la salida:

'1' = 'uno'

'1' = 'un compuesto de bis'

'2' = 'dos'

'2' = 'otro valor para dos'

'3' = 'tres'

7

lo hice mediante la creación de un . Cada vez que encuentro la clave duplicada, simplemente inserto el valor en la lista existente asociada con la clave ya presente en el objeto SortedList. De esta manera, puedo tener una lista de valores para una clave en particular.

+2

es bastante increíble que no haya una lista ordenada real en C# ... –

+0

@ BlueRaja-DannyPflughoeft: Hay una 'SortedList', pero no permite duplicar claves. Y tenga en cuenta que mis preguntas eran específicas de .NET 2.0. De todos modos, desde .NET 3.5 en adelante, el mismo problema se puede resolver usando el 'Lookup' en Linq. Vea este enlace - http://msdn.microsoft.com/en-us/library/bb460184.aspx. – Learner

+3

Conozco SortedList y Lookup. Pero estos son mapas, no listas. No hay una lista ordenada real en C#. Hay 'List.Sort()', pero luego insertando y luego ordenando la lista es una operación 'O (n log n)', mientras que debería ser simplemente 'O (log n)' o 'O (n)' En el peor de los casos. –

0

En .NET 2.0 se puede escribir:

List<KeyValuePair<string, string>> keyValueList = new List<KeyValuePair<string, string>>(); 

// Simulate your list of key/value pair which key could be duplicate 
keyValueList.Add(new KeyValuePair<string,string>("1","One")); 
keyValueList.Add(new KeyValuePair<string,string>("2","Two")); 
keyValueList.Add(new KeyValuePair<string,string>("3","Three")); 

// Here an entry with duplicate key and new value 
keyValueList.Add(new KeyValuePair<string, string>("2", "NEW TWO")); 

// Your final sorted list with one unique key 
SortedList<string, string> sortedList = new SortedList<string, string>(); 

foreach (KeyValuePair<string, string> s in keyValueList) 
{ 
    // Use the Indexer instead of Add method 
    sortedList[s.Key] = s.Value; 
} 

Salida:

[1, One] 
[2, NEW TWO] 
[3, Three] 
1

¿Qué tal esto

 SortedList<string, List<string>> sl = new SortedList<string, List<string>>(); 

     List<string> x = new List<string>(); 

     x.Add("5"); 
     x.Add("1"); 
     x.Add("5"); 
     // use this to load 
     foreach (string z in x) 
     { 
      if (!sl.TryGetValue(z, out x)) 
      { 
       sl.Add(z, new List<string>()); 
      } 

      sl[z].Add("F"+z); 
     } 
     // use this to print 
     foreach (string key in sl.Keys) 
     { 
      Console.Write("key=" + key + Environment.NewLine); 

      foreach (string item in sl[key]) 
      { 
       Console.WriteLine(item); 
      } 
     } 
+0

Gracias por mirar la pregunta. Pero 'SortedList' no sirve de nada. Como mencioné en la pregunta en sí, tendré llaves duplicadas por algún motivo y 'SortedList' no permite llaves duplicadas. – Learner

+0

@CSharpLearner, esta respuesta no usa claves duplicadas. Los elementos con claves duplicadas se agregan a una lista, de modo que si repites las teclas obtendrás una lista de claves únicas. Para cualquier clave, puede tener 1 o más valores porque cada valor es una lista en sí misma. Lo que significa que es una lista ordenada de la lista de valores. –

5

Use su propia clase comparador! Si sus llaves en la lista ordenada son números enteros, es posible utilizar, por ejemplo, este comparador:

public class DegreeComparer : IComparer<int> 
{ 
    #region IComparer<int> Members 

    public int Compare(int x, int y) 
    { 
     if (x < y) 
      return -1; 
     else 
      return 1; 
    } 

    #endregion 
} 

Para instanciar un nuevo SortedList con claves int y los valores de cadena utilizan:

var mySortedList = new SortedList<int, string>(new DegreeComparer()); 
0

que tenía una problema similar en el que estaba diseñando un juego similar al concepto de un juego de ajedrez en el que tienes que hacer que la computadora se mueva.Necesitaba tener la posibilidad de que varias piezas fueran capaces de hacer un movimiento y, por lo tanto, necesitaba tener múltiples Estados de Junta. Cada BoardState necesitaba clasificarse según la posición de las piezas. Por el bien de los argumentos y la simplicidad, digamos que mi juego era Noughts and Crosses, y Noughts y Computer was Crosses. Si el estado del tablero mostraba 3 en una fila de Noughts, este es el mejor estado para mí, si muestra 3 en una fila de Cruces, entonces este es el peor estado para mí y el mejor para la computadora. Hay otros estados durante el juego que son más favorables para uno u otro y, además, hay estados de muliplé que dan como resultado un sorteo, entonces, ¿cómo hago para clasificarlo cuando hay puntajes de rango iguales? Esto es lo que se me ocurrió (discúlpate por adelantado si no eres un programador de VB).

Mi clase comparador:

Class ByRankScoreComparer 
    Implements IComparer(Of BoardState) 

    Public Function Compare(ByVal bs1 As BoardState, ByVal bs2 As BoardState) As Integer Implements IComparer(Of BoardState).Compare 
     Dim result As Integer = bs2.RankScore.CompareTo(bs1.RankScore) 'DESCENDING order 
     If result = 0 Then 
      result = bs1.Index.CompareTo(bs2.Index) 
     End If 
     Return result 
    End Function 
End Class 

Mis declaraciones:

Dim boardStates As SortedSet(Of BoardState)(New ByRankScoreComparer) 

Mi Junta-Estado la ejecución:

Class BoardState 
    Private Shared BoardStateIndex As Integer = 0 
    Public ReadOnly Index As Integer 
    ... 
    Public Sub New() 
     BoardStateIndex += 1 
     Index = BoardStateIndex 
    End Sub 
    ... 
End Class 

Como se puede ver RankScores se mantienen en orden y cualquier descendente 2 estados que tienen el mismo puntaje de rango el último estado va al fondo ya que siempre tendrá un gran El índice asignado y, por lo tanto, esto permite duplicados. También puedo llamar a boardStates.Remove (myCurrentBoardState) de forma segura, que también usa el comparador y el comparador debe devolver un valor de 0 para localizar el objeto que se va a eliminar.

Cuestiones relacionadas