2015-10-26 4 views
6

Sto lavorando al seguente codice per trovare una "descrizione" basata su una frase di ricerca che l'utente specificherà. Pensa alle Chiavi come frasi importanti e ai Valori come descrizione su dove andare per trovare quella frase.
Pensa ad esempio a uno store locator (la migliore analogia che posso immaginare). Se cerchi "Target" (chiave), otterrai tonnellate di città (valore) e alcune potrebbero avere lo stesso nome. Quindi possono esserci più destinazioni nella stessa città.
A Dictionary ovviamente non funzionerà, perché avrò nomi di negozi duplicati o forse duplichi nomi di città. Il che mi porta alla mia situazione attuale:Linq alternativa al recupero dei valori di ricerca per chiave (o chiave parziale)

Fondamentalmente, io sto partendo da una List<KeyValuePair<string, string>> per consentire i duplicati in entrambe le direzioni, quindi la conversione che a un Lookup<string, string> che ho assunto sarebbe meno confusione di quello che si sta rivelando di essere.

List<KeyValuePair<string, string>> kvpList = new List<KeyValuePair<string, string>>(); 
Lookup<string, string> collection; 

kvpList.Add(new KeyValuePair<string, string>("K1", "R1")); 
kvpList.Add(new KeyValuePair<string, string>("K1", "R1")); 
kvpList.Add(new KeyValuePair<string, string>("K1", "R2")); 
kvpList.Add(new KeyValuePair<string, string>("K2", "R1")); 
kvpList.Add(new KeyValuePair<string, string>("K2", "R2")); 
kvpList.Add(new KeyValuePair<string, string>("K2", "R3")); 
kvpList.Add(new KeyValuePair<string, string>("K2", "R1")); 

collection = (Lookup<string,string>)kvpList.ToLookup(k => k.Key, k => k.Value); 

È possibile che questo è semplicemente falso informazioni di prova, ma mi sento come se ci deve essere un modo più pulito per ottenere i risultati dal Lookup soprattutto perché sembra essere molto Linq-friendly. Sfortunatamente, non ho molta familiarità con Linq, e la sintassi di Linq non sembra prestarsi ad essere molto adatta ai principianti. Mi vengono i risultati in questo codice (termine di ricerca hard coded solo a scopo di test):

string searchTerm = "K2"; 
List<string> uniqueResults = new List<string>(); 

foreach (var item in collection) 
{ 
    if (item.Key.Contains(searchTerm)) 
    { 
     foreach (var value in item) 
     { 
      if (!uniqueResults.Contains(value)) 
      { 
       uniqueResults.Add(value); 
       Console.WriteLine("Added: " + value); 
      } 
      else 
      { 
       Console.WriteLine("Skipped duplicate: " + value); 
      } 
     } 
    } 
} 

Non ho problemi con il codice di cui sopra, ma la mia domanda è: esiste un modo per utilizzare Linq per raggiungere quello che sto cercando di realizzare? Mi sento come quello che ho non è il modo migliore in cui questo potrebbe essere fatto ...
Può essere interessante notare che un numero parziale searchTermdeve essere in grado di trovare risultati dove si verifica nella chiave (quindi contiene). Le risposte esistenti non erano in grado di rispondere alle mie domande specifiche. Non ero in grado di trovarne uno per aiutarmi a ottenere valori tramite una ricerca parziale delle chiavi.

+0

Una ricerca è come un dizionario, non c'è ricerca di sottostringa. Non è possibile utilizzare un approccio basato su set se si ha bisogno di una ricerca di sottostringa perché usano 'GetHashCode' che non può gestire parte di stringhe (come potrebbe restituire un singolo valore per tutte le sottostringhe?). –

+0

Suggerisco di creare "una ricerca parziale. Termerà inoltre di poter trovare risultati dove si verifica nella chiave" molto più importante nella tua domanda, cambiando il titolo e rendendolo parte dei primi paragrafi. Dà forma alla domanda un bel po ', penso. – 31eee384

+0

Grazie @ 31eee384, apportato le modifiche. –

risposta

6

Il codice tradotto in LINQ assomiglia a questo:

var uniqueResults = collection 
     .Where(item => item.Key.Contains(searchTerm)) // filter the collection 
     .SelectMany(x => x)       // flatten results 
     .Distinct()         // remove duplicates 
     .ToList(); 

Non hai nemmeno serve il Lookup. È possibile ottenere gli stessi risultati con kvpList:

var uniqueResults = kvpList 
     .Where(item => item.Key.Contains(searchTerm)) // filter the collection 
     .Select(item => item.Value)     // get the Values from KeyValuePairs 
     .Distinct()         // remove duplicates 
     .ToList(); 

La soluzione LINQ è in realtà molto più facile da capire rispetto a quella imperativo. Prova a descrivere il tuo algoritmo in inglese: da kvpList, seleziona valori distinti in cui la chiave contiene il termine di ricerca. Questo è quasi esattamente il secondo codice LINQ.

+3

La ricerca è inutile qui. OP dovrebbe usare solo 'kvpList' (quindi sostituire' collection' con 'kvpList') –

+0

@TimSchmelter Hai ragione, ed è ancora più facile da capire rispetto alla versione con lookup. Ho incluso entrambe le versioni nella risposta. –

+0

Grazie a @JakubLortz! Questo rende molto più facile capire il modo LINQ per farlo. Sicuramente più facile da capire con il contesto. –

1

Si potrebbe usare LINQ e un HashSet<string>, che eliminerebbe i duplicati per voi:

var uniqueResults = collection.Where(item => item.Contains(searchTerm)) 
           .SelectMany(x => x) 
           .ToHashSet(); 

Dove ToHashSet è un metodo di estensione personalizzata è possibile creare facilmente:

public static class EnumerableExtensions 
{ 
    public static HashSet<T> ToHashSet(this IEnumerable<T> enumerable) 
    { 
     return new HashSet<T>(enumerable); 
    } 
} 
+0

Ti manca un livello di appiattimento. 'uniqueResults' nella domanda contiene i valori, non una enumerabile di HashSet. – CoderDennis

+0

@CoderDennis Grazie, mi sono perso. –

+1

Non * abbastanza * semplice come la risposta di Jakub, ma comunque molto interessante! +1 da me. –

Problemi correlati