2012-12-31 17 views
6

Mam plik csv z 30 000 linii. Muszę wybrać wiele wartości w oparciu o wiele warunków, więc w przypadku wielu pętli i "jeśli" zdecydowałem się użyć linq. Napisałem klasę do odczytu csv. Implementuje IEnumerable do użycia z linq. To jest mój moduł wyliczający:Czy można zmienić metodę wyszukiwania w LINQ?

class CSVEnumerator : IEnumerator 
{ 

    private CSVReader _csv; 

    private int _index; 

    public CSVEnumerator(CSVReader csv) 
    { 
     _csv = csv; 
     _index = -1; 
    } 

    public void Reset(){_index = -1;} 


    public object Current 
    { 
     get 
     { 
      return new CSVRow(_index,_csv); 
     } 
    } 


    public bool MoveNext() 
    { 
     return ++_index < _csv.TotalRows; 
    } 

} 

Działa, ale działa wolno. Powiedzmy, że chcę wybrać wartość maksymalną w kolumnie A w zakresie 100; 150 wierszy.

max = (from CSVRow r in csv where r.ID > 100 && r.ID < 150 select r).Max(y=>y["A"]); 

to będzie działać, ale wyszukiwania LINQ do wartości max w 30 000 wierszy zamiast 48. Jak powiedziałem, mogę użyć pętli, ale tylko w tym przykładowym przypadku, warunki są „brutalne” :)

Czy istnieje sposób na zastąpienie wyszukiwania kolekcji linq. Coś jak: spójrz na kwerendę używaną na moim module wyliczającym, zobacz, czy jakiekolwiek warunki linq w "where" zawiera "filtr ID wiersza" i podaj inne dane w oparciu o to.

Nie chcę kopiować części danych do innej tablicy/kolekcji i problem nie występuje w moim czytniku csv. Uzyskiwanie dostępu do każdego wiersza według identyfikatora jest szybkie, a jedynym problemem jest dostęp do wszystkich 30 000 z nich. Każda pomoc uzyskała :-)

+1

BTW, powinieneś zaimplementować 'IEnumerable '. – SLaks

+0

W jaki sposób jest to zaimplementowane -> 'new CSVRow (_index, _csv)'. Dostęp losowy lub sekwencyjny? – Tilak

+1

Czy jesteś pewien, że linq to objects stosuje Max do wszystkich twoich rekordów, a nie tylko do tych 48? Dziwne, powinno stosować operatorów kolejno. Domyślam się, że twój moduł wyliczający jest po prostu powolny. –

Odpowiedz

2

Jeśli chciałbyś móc efektywnie używać LINQ, musiałbyś użyć expression trees, w podobny (ale znacznie prostszy) sposób, niż robią to różni dostawcy LINQ dla baz danych SQL. Chociaż jest to wykonalne, myślę, że byłoby to dość dużo kodu dla tak prostego zadania.

Z tego powodu, myślę, że lepszym rozwiązaniem byłoby użycie osobnej metody wyboru żądanych rzędów (a następnie użycie LINQ do pracy z wynikiem).

Ponadto wiele operacji, które zwracają kolekcje (w tym oryginalny kod i moją modyfikację), można uprościć, stosując iterator methods.

Tak, kod może wyglądać mniej więcej tak:

public static IEnumerable<CSVRow> GetRows(
    this CSVReader reader, int idGreaterThan, int idLessThan) 
{ 
    for (int i = idGreaterThan + 1; i < idLessThan; i++) 
    { 
     yield return new CSVRow(reader, i); 
    } 
} 

Tutaj jest to metoda rozszerzenie dla CSVReader, ale innego rozwiązania (np rzeczywista metoda na tej klasy) może być bardziej odpowiedni dla Ciebie.

Wasz przykład będzie wtedy wyglądać tak:

max = csvReader.GetRows(100, 150).Max(y => y["A"]); 

(Również uważam, że to dziwne, że gdy masz limitów 100 i 150, rzeczywiście chcesz wiersze między 101 i 149. Ale ja zakładając mam powód do tego, więc zrobiłem to samo.)

+0

To jest dokładnie to, czego potrzebuję, dziękuję :-) 100 i 150 było tylko przykładem, aby pokazać problem, jeśli zamieszczam prawdziwy warunek, musiałbym wyjaśnić, skąd pochodzą zmienne, jakie typy danych są itp ... –

1

Jeśli chodzi o LINQ, r.ID jest po prostu wartością, która jest filtrowana, a więc wszystkie 30k linii są uważane za użyteczne w operacji Max. Jeśli jest to indeks wiersza, który wydaje się mieć tutaj miejsce, możesz użyć Pomiń i Wykonaj, aby uniknąć porównywania wszystkich 30-krotnych wierszy.

max = csv.Skip(100).Take(50).Max(y => y["A"]); 
+0

Ale to będzie nadal iterować 150 wierszy, nie ma 50. Tak więc, jeśli zakres wynosił 29000-29050, zrobiłbyś iterację 29050 wierszy, co jest bardzo nieefektywne. – svick

+0

@svik: Jak twój CSVReader wie, w którym rzędzie jest, jeśli pomijasz pierwsze 29000? Nadal musisz przeczytać wszystkie z nich przed, więc wybierasz właściwe linie. Wydaje mi się, że implementacja CSVReader jest nieefektywna. Powinien buforować już przeczytane wiersze, a wtedy prawie wszystkie zapytania będą szybkie. –

+0

@AisisKraus Nie mam pojęcia, w jaki sposób zaimplementowano 'CSVReader', ale pytanie mówi, że efektywnie pobiera każdy pojedynczy wiersz. – svick

0

@DougM ma rację kolejności oceny, ale w tym przypadku to, co chciałbym zrobić, to wziąć jeden raz uderzony w inicjalizacji i generują wyszukiwań za „Index” pól: w zasadzie, wstępnie obliczyć mapy (słownik) indeksu wiersza do wiersza. To powiedziawszy, byłoby to przydatne tylko wtedy, gdy masz wiele powtarzających się zapytań dla danego pola indeksu.

Powiązane problemy