2017-05-30 90 views
5

Mam listę ciągów gdzie każdy element jest dowolny tekst opisujący umiejętności, więc wygląda trochę tak:
wyszukiwania kluczy słownikowych zawartych w tablicy ciągów

List<string> list = new List<string> {"very good right now", "pretty good", 
"convinced me that is good", "pretty medium", "just medium" .....} 

I chcę zachować użytkownikowi wynik dla tych darmowych tekstów. Więc teraz, używam warunki:

foreach (var item in list) 
     { 
      if (item.Contains("good")) 
      { 
       score += 2.5; 
       Console.WriteLine("good skill, score+= 2.5, is now {0}", score); 
      } 
      else if (item.Contains(low")) 
      { 
       score += 1.0; 
       Console.WriteLine("low skill, score+= 1.0, is now {0}", score); 
      } 

     } 

Załóżmy W furure chcę użyć słownika do mapowania score, takich jak:

Dictionary<string, double> dic = new Dictionary<string, double> 
{ { "good", 2.5 }, { "low", 1.0 }}; 

Jaki byłby dobrym sposobem na skrzyżowanie wartości słownikowe i lista ciągów znaków? Teraz widzę, jak to wygląda: zagnieżdżona pętla:

foreach (var item in list) 
     { 
      foreach (var key in dic.Keys) 
       if (item.Contains(key)) 
        score += dic[key]; 
     } 

Ale jestem pewien, że są lepsze sposoby. Lepiej być szybszym lub bardziej przyjemnym dla oka (LINQ) przynajmniej.

Dzięki.

+0

wątpię, że można to zrobić szybciej od większości innych sposobów (LINQ lub nie) będzie prawdopodobnie robi zagnieżdżone pętle za kulisami. Poza tym, jeśli nie masz tysięcy umiejętności, różnica w wydajności prawdopodobnie byłaby znikoma. IMHO, twoje proponowane rozwiązanie nie jest tak nieprzyjemne na oku :) – HaukurHaf

+0

"Powodzenia w tym błazenie robić coś pożytecznego" -> zarówno "dobry" i "użyteczny". Powiedział, że twój projekt wydaje mi się rozsądny i mam nadzieję, że będę mógł czerpać korzyści z następnego przeglądu wydajności. –

+0

Pamiętaj, że podejście ze słownikiem nie jest takie samo, jak podejście oryginalne, ponieważ dodajesz wynik dla każdego klucza w słowniku –

Odpowiedz

2
var scores = from item in list 
      from word in item.Split() 
      join kvp in dic on word equals kvp.Key 
      select kvp.Value; 

var totalScore = scores.Sum(); 

Uwaga: obecne rozwiązanie sprawdza, czy pozycja na liście zawiera klucz w słowniku. Ale zwróci true, nawet jeśli klucz w słowniku jest częścią jakiegoś słowa w przedmiocie. Na przykład. "follow the rabbit" zawiera "low". Dzielenie przedmiotu na słowa rozwiązuje ten problem.

Również łączenie LINQ wykorzystuje wewnętrznie funkcję mieszania, aby wyszukiwać elementy pierwszej sekwencji w drugiej sekwencji. To daje O (1) szybkość wyszukiwania zamiast O (N), gdy wyliczasz wszystkie pozycje słownika.

+1

Preferowałem ten, ponieważ również oświeciłeś mnie o dzieleniu słów. Jednak nie jestem zaznajomiony z tą składnią. Czy jest to również uważane za LINQ? –

+0

@yoadw tak, to jest LINQ napisany w * składni zapytania * (składnia zintegrowana z językiem) –

1

naprawdę jej nie szybciej, ale można użyć LINQ:

score = list.Select(s => dic.Where(d => s.Contains(d.Key)) 
      .Sum(d => d.Value)) 
      .Sum(); 

pamiętać, że przykładem pętli trafi 2 różne klucze jeśli string pasuje zarówno Ciągle że w moim rozwiązaniu.

2

Jeśli twój kod znajdzie N ciągi znaków umiejętności zawierających słowo "dobry", to dołącza wynik 2,5 N razy.

Możesz więc liczyć łańcuchy umiejętności zawierające słownik i pomnożyć wartość na odpowiednim wyniku.

var scores = from pair in dic 
      let word = pair.Key 
      let score = pair.Value 
      let count = list.Count(x => x.Contains(word)) 
      select score * count; 

var totalScore = scores.Sum(); 
0

Cóż, tak naprawdę nie są za pomocą słownika jak słownik, więc możemy uprościć ten kawałek z nowej klasy:

class TermValue 
{ 
    public string Term { get; set; } 
    public double Value { get; set; } 

    public TermValue(string t, double v) 
    { 
     Term = t; 
     Value = v; 
    } 
} 

z tym, możemy być nieco bardziej bezpośredni :

void Main() 
{ 
    var dic = new TermValue[] { new TermValue("good", 2.5), new TermValue("low", 1.0)}; 


    List<string> list = new List<string> {"very good right now", "pretty good", 
"convinced me that is good", "pretty medium", "just medium" }; 

    double score = 0.0; 
    foreach (var item in list) 
    { 
     var entry = dic.FirstOrDefault(d =>item.Contains(d.Term)); 
     if (entry != null) 
      score += entry.Value; 
    } 
} 

Stąd możemy grać tylko trochę (kod skompilowany za to będzie prawdopodobnie taki sam jak wyżej)

double score = 0.0; 
    foreach (var item in list) 
    { 
     score += dic.FirstOrDefault(d =>item.Contains(d.Term))?.Value ?? 0.0; 
    } 

następnie (w słowie Purple One), możemy zaszaleć:

double score = list.Aggregate(0.0, 
    (scre, item) =>scre + (dic.FirstOrDefault(d => item.Contains(d.Term))?.Value ?? 0.0));