2012-04-23 1 views
8

Ho il seguente elenco di distinti stringhe:Esiste un modo migliore per restituire l'elemento successivo in un elenco e passare dalla fine alla parte anteriore?

"A"
"B"
"C"

Se voglio la voce dopo A, I get B. Dopo B, Ricevo C. Dopo C, ottengo A. Attualmente ho il seguente codice, ma per qualche ragione mi sembra che ci sia un modo migliore per farlo (forse?).

private string GetNext(IList<string> items, string curr) 
{ 
    if (String.IsNullOrWhitespace(curr)) 
     return items[0]; 

    var index = items.IndexOf(curr); 
    if (index == -1) 
     return items[0]; 

    return (index + 1 == items.Count) ? items[0] : items[index + 1]; 
} 

Sono sicuramente aperto a un modo LINQ-esque di fare questo anche :)

+0

Penso che questo: è http://stackoverflow.com/questions/716256/creating-a-circually-linked-list-in-c cosa stai cercando – hyp

+0

@hyp: Beh, questo funzionerebbe se il parametro che si sta passando fosse un 'CircularLinkedList ', ma è solo un 'IList '. –

risposta

6

Penso che forse è possibile modificare la linea di

return (index + 1 == items.Count) ? items[0] : items[index + 1]; 

per qualcosa come

return items[(index + 1) % items.Count]; 
+1

Buon miglioramento, lo prendo. Anche se dovrebbe essere (indice + 1) giusto? –

+0

Non penso, perché nel tuo array Count = 3 e quando ottieni Index = 3 restituirà 3% 3 == 0. Quando ottieni index = 2 (per restituire 'C') lo otterrai correttamente 2% 3 = 2 – Andres

+0

Sì, hai ragione. – Andres

0

È possibile utilizzare l'operatore mod per semplificare un po 'e combinare tutto in un'unica istruzione:

return items[((String.IsNullOrWhitespace(curr) 
       ? 0 
       : items.IndexOf(curr)) + 1) % items.Count] 

sua sicuramente più breve, ma non sono sicuro che la sua climatiche più leggibile, anche :)

1

posso vedere qualche ottimizzazione se si traccia l'indice corrente piuttosto che la stringa corrente, ma per fare che la la lista degli articoli dovrebbe essere riparata, cioè non cambiata.

Si potrebbe anche return items[(index + 1) % items.Count];

Altrimenti quel codice guarda bene a me, ma forse qualcuno ha una soluzione più intelligente.

7

La soluzione che hai è funzionalmente corretta ma le prestazioni lasciano un po 'a desiderare. Tipicamente quando si ha a che fare con una struttura di stile di lista ci si aspetterebbe che GetNext restituisca un risultato in tempo O (1), tuttavia questa soluzione è O (N).

public sealed class WrappingIterator<T> { 
    private IList<T> _list; 
    private int _index; 
    public WrappingIterator<T>(IList<T> list, int index) { 
    _list = list; 
    _index = index; 
    } 
    public T GetNext() { 
    _index++; 
    if (_index == _list.Count) { 
     _index = 0; 
    } 
    return _list[_index]; 
    } 

    public static WrappingIterator<T> CreateAt(IList<T> list, T value) { 
    var index = list.IndexOf(value); 
    return new WrappingIterator(list, index); 
    } 
} 

La chiamata iniziale a CreateAt è O (N) qui ma chiamate successive GetNext sono O (1).

IList<string> list = ...; 
var iterator = WrappingIterator<string>.CreateAt(list, "B"); 
Console.WriteLine(iterator.GetNext()); // Prints C 
Console.WriteLine(iterator.GetNext()); // Prints A 
Console.WriteLine(iterator.GetNext()); // Prints B 
+1

Questo è un modo molto interessante per farlo. Un po 'più complicato di quanto vorrei, ma sicuramente interessante. –

+0

Inoltre, solo una FYI. L'unica ragione per cui non ho accettato questa risposta è perché la funzione accetta un 'IList ' e non volevo creare un nuovo iteratore per ogni chiamata alla funzione. –

1

LINQ non è lo strumento appropriato qui.

Sembra come se un LinkedList<T> sarebbe la migliore raccolta qui:

var linkedItems = new LinkedList<String>(items); 
LinkedListNode current = linkedItems.Find("C"); 
String afterC = current.Next == null ? linkedItems.First.Value : current.Next.Value; 

Here sono i pro ei contro di una LinkedList rispetto a un elenco.

1

Un modo LINQ:

var result = (from str in list 
       let index = list.IndexOf(curr) + 1 
       select list.ElementAtOrDefault(index) ?? list[0]).First();