2012-01-18 16 views
7

Mam List<string>, a niektóre z tych ciągów są liczbami. Chcę wyodrębnić ten podzestaw do List<int>.Używanie LINQ do wyodrębniania ints z listy ciągów znaków

Zrobiłem to w dość gadatliwy sposób - jak poniżej - ale mam wrażenie, że musi istnieć lepszy sposób LINQ, aby to ukształtować. Jakieś pomysły?

List<string> myStrs = someListFromSomewhere; 
List<int> myInts = new List<int>(); 

foreach (string myStr in myStrs) 
{ 
    int outInt; 
    if (int.TryParse(myStr, out outInt)) 
    { 
     myInts.Add(outInt); 
    } 
} 

Oczywiście nie trzeba rozwiązanie to - to głównie dla mojego wykształcenia LINQ.

+2

Kolejna okazja dla mnie do państwa "Życzę TryParse wróci int? (Wiem, dziedzictwo ...) –

+0

z pewnością nie bardziej wydajne, ale można zrobić "var myInts = myStrs.Where (s => int.TryParse (s, out out)). Wybierz (s => int.Parse (s)) ', o ile już zdążyłeś zdefiniować. Nazywa to 'TryParse' * i * Parse na każdym łańcuchu, więc tak naprawdę nie sugerowałbym tego. – rejj

+2

To pytanie może być interesujące: [zapytanie LINQ do wykonania projekcji, pomijając przypadki, w których projekcja spowodowałaby wyjątek] (http://stackoverflow.com/questions/7188623/linq-query-to-perform-a-projection-skipping-cases-where-the-projection-would-ca) Używam twojego dokładnego przypadku jako przykładu. –

Odpowiedz

23

Można to zrobić tak:

int parsed = 0; 

myInts = myStrs.Where(x => int.TryParse(x, out parsed)).Select(x => parsed); 

To działa, ponieważ wykonanie operatorów LINQ jest odroczona, to znaczy:
Dla każdej pozycji w myStrs pierwszy kod w Where jest wykonywany, a wynik zapisany do parsed. A jeśli TryParse zwróci true, zostanie wykonany kod w Select. Cały ten kod dla jednego produktu działa przed uruchomieniem całego kodu dla następnego elementu.

+3

Wygląda dla mnie bardzo krucho. – dtb

+1

@dtb: Wcale nie. Jeśli tak, to tak naprawdę nie rozumiesz wewnętrznych mechanizmów LINQ. –

+1

Po prostu mówię, że wymaga on zbyt dużej wiedzy o wewnętrznych działaniach LINQ na mój gust. – dtb

7

Parametry LINQ i out nie mieszają się dobrze. Można to zrobić:

var myInts = myStrs 
    .Select(s => 
    { 
     int outInt; 
     return int.TryParse(s, out outInt) ? (int?)outInt : null; 
    }) 
    .Where(i => i.HasValue) 
    .Select(i => i.Value) 
    .ToList(); 
+0

+1 - Dokładnie rozwiązanie, które miałem zamiar opublikować. –

+4

Bezwzględna przesada –

+0

Czy to * naprawdę * zasługuje na awans? To nie jest złe? – Yuck

0

Ponieważ jest to dla edukacji LINQ ... Jeśli naprawdę szukasz, jak można to zrobić tylko składni LINQ, Innym rozwiązaniem jest przeniesienie logikę parsowania do klasy, która kapsułkuje wynik i wartość.

var myInts = from val in myStrs 
      let parserVal = new Parser(val) 
      where parserVal.IsInt 
      select parserVal.Val; 

gdzie Parser jest coś takiego ...

class Parser 
{ 
     public bool IsInt { get; set; } 
     public int Val { get; set; } 

     public Parser(string val) 
     { 
      int outVal; 
      IsInt = int.TryParse(val, out outVal); 
      if (IsInt) 
      { 
       Val = outVal; 
      } 
     } 
} 
Powiązane problemy