2010-08-05 20 views
12

Ho un set di elementi/chiavi che sto leggendo da due diversi file di configurazione. Quindi le chiavi possono essere uguali ma con valori diversi associati a ciascuna di esse.Come ordinare l'elenco con chiavi duplicate?

Voglio elencarli nell'ordine ordinato. Cosa posso fare ? Ho provato con la classe SortedList ma non consente chiavi duplicate.

Come posso farlo?

e.g dire che ho 3 elementi con i tasti 1,2,3. Poi ho un altro elemento che ha la chiave 2 (ma un valore diverso). Poi voglio che la nuova chiave venga inserita dopo la chiave 2 esistente ma prima 3. Se trovo un elemento con la chiave 2, allora dovrebbe andare dopo la chiave aggiunta più di recente 2.

Si prega di notare che sto usando. NET 2.0

+0

Ti interessa davvero se gli elementi con chiavi uguali vanno prima o dopo gli elementi esistenti? – BlueMonkMN

+0

Sì. Voglio mantenere l'ordine come indicato nella mia domanda – Learner

risposta

12

preferisco usare LINQ per questo tipo di cosa:

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: è necessario utilizzare .NET 3.5 o versione successiva per raggiungere questo obiettivo.

+0

Grazie, ma sto usando .NET 2.0 – Learner

+0

Yuck. Questo da solo è una ragione sufficiente per l'aggiornamento. –

+1

questo non è possibile quando si utilizza .net 2.0 quindi non è una risposta alla sua domanda – Nealv

1

.NET non ha un supporto enorme per tipi stabili (nel senso che gli elementi equivalenti mantengono il loro ordine relativo quando ordinati). Tuttavia, è possibile scrivere il proprio inserto stabili-ordinati utilizzando List.BinarySearch e un numero personalizzato IComparer<T> (che restituisce -1 se la chiave è inferiore a o uguale a destinazione e +1 se maggiore).

Si noti che List.Sort non è un ordinamento stabile, quindi è necessario scrivere la propria routine di quicksort stabile o semplicemente utilizzare l'ordinamento di inserimento per popolare inizialmente la raccolta.

9

ciò di cui hai bisogno è una funzione di ordinamento con un numero personalizzato IComparer. Quello che hai ora è l'icomparer predefinito quando usi l'ordinamento. questo controllerà il valore di un campo.

Quando si crea un IComparer personalizzato (lo si fa nella classe utente implementando l'interfaccia Icomparable). ciò che fa è: il tuo oggetto si controlla su ogni altro oggetto della lista che ordini.

questo è fatto da una funzione. (non ti preoccupare VS lo implementerà quando fai riferimento alla tua interfaccia

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"); 
     } 
} 

leggere i link sopra per una migliore informazione.

So che ho avuto qualche problema di comprensione questo io stesso all'inizio, quindi per qualsiasi aiuto in più aggiungere un commento e mi dilungherò

2

Se non si ha realmente a cuore la sequenza degli elementi con chiavi uguali, aggiungi tutto a una lista e poi ordinarlo tramite chiave:

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); 
} 

Se si vuole veramente gli elementi inseriti in seguito ad essere, dopo quelli inseriti in precedenza, ordinarli in cui sono inseriti:

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 
} 

Oppure si potrebbe avere un elenco ordinato di liste:

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()); 
    } 
} 

io non sono sicuro se è possibile utilizzare inizializzatori Elenco in NET 2.0 come ho fatto nel primo esempio di cui sopra, ma sono sicuro che si sa come compilare un elenco con dati.

0

avete contemplato la classe NameValueCollection in quanto consente di memorizzare più valori per chiave? si potrebbe ad esempio avere il seguente:

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"); 

e poi per recuperare i valori si potrebbe avere:

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]); 
     } 

    } 

che danno l'uscita:

'1' = 'uno'

'1' = 'uno bis'

'2' = 'due'

'2' = 'altro valore per due'

'3' = 'tre'

7

ho fatto che creando un . Ogni volta che trovo la chiave duplicata, inserisco semplicemente il valore nell'elenco esistente associato alla chiave già presente nell'oggetto SortedList. In questo modo, posso avere un elenco di valori per una particolare chiave.

+2

è piuttosto incredibile che non ci sia una lista ordinata in C# ... –

+0

@ BlueRaja-DannyPflughoeft: esiste un 'SortedList', ma non consente chiavi duplicate. E nota che le mie domande erano specifiche per .NET 2.0. Ad ogni modo, da .NET 3.5 in poi, lo stesso problema può essere risolto usando il 'Lookup' in Linq. Vedi questo link - http://msdn.microsoft.com/en-us/library/bb460184.aspx. – Learner

+3

Sono a conoscenza di SortedList e Lookup. Ma queste sono entrambe mappe, non liste. Non esiste una lista ordinata in C#. Esiste 'List.Sort()', ma poi inserire-then-sorting l'elenco è un'operazione 'O (n log n)', mentre dovrebbe essere semplicemente 'O (log n)' o 'O (n)' Nel peggiore dei casi. –

0

In .NET 2.0 è possibile scrivere:

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; 
} 

uscita:

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

ne dite di questo

 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

Grazie per aver guardato la domanda. Ma 'SortedList' è inutile. Come menzionato nella domanda stessa, avrò chiavi duplicate per qualche motivo e 'SortedList' non consente chiavi duplicate. – Learner

+0

@CSharpLearner, questa risposta non utilizza chiavi duplicate. Gli elementi con chiavi duplicate vengono aggiunti a un elenco, quindi se si scorre le chiavi si ottiene un elenco di chiavi univoche. Per qualsiasi chiave potresti avere 1 o più valori perché ogni valore è una lista di per sé. Significa che è una lista ordinata di lista per valori. –

5

Utilizzare la propria classe di confronto! Se le chiavi nella lista ordinata sono interi, si può utilizzare ad esempio questo di confronto:

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 
} 

Per instanciate una nuova SortedList con le chiavi int e valori di stringa utilizzano:

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

Ho avuto un problema simile a quello in cui stavo progettando un gioco simile al concetto di un gioco di scacchi in cui il computer ha fatto una mossa.Avevo bisogno di avere più pezzi in grado di fare una mossa e quindi avevo bisogno di avere più Board-States. Ogni BoardState doveva essere classificato in base alla posizione dei pezzi. Per amor di discussione e semplicità, dì che il mio gioco era Noughts and Crosses e I was tris e Computer's Crosses. Se lo stato di bordo mostrava 3 di fila di Nough, questo è lo stato migliore per me, se mostra 3 di fila di Crosses, questo è lo stato peggiore per me e il migliore per il computer. Ci sono altri stati durante il gioco che sono più favorevoli all'uno o all'altro e inoltre ci sono stati muliplte che danno come risultato un Draw, quindi come faccio a classificarlo quando ci sono punteggi di rango uguali. Questo è quello che mi è venuto in mente (chiedi scusa in anticipo se non sei un programmatore VB).

La mia classe di confronto:

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 

mie dichiarazioni:

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

Il mio consiglio-Stato di attuazione:

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

Come si può vedere RankScores sono mantenuti in ordine e qualsiasi decrescente 2 stati che hanno lo stesso punteggio, lo stato successivo va verso il basso in quanto avrà sempre un grande er assegnato Index e quindi ciò consente duplicati. Posso anche chiamare in modo sicuro boardStates.Remove (myCurrentBoardState) che utilizza anche il comparatore e il comparatore deve restituire un valore 0 per individuare l'oggetto da eliminare.

Problemi correlati