Najefektywniejszy sposób usuwania duplikatów z listy obiektów

Najefektywniejszy sposób usuwania duplikatów z listy obiektów
DR
  • Rejestracja:ponad 13 lat
  • Ostatnio:prawie 4 lata
  • Postów:37
0

Witam,
w jaki sposób najefektywniej mogę usunąć duplikaty z DUŻEJ listy typu <MójObiekt>, sprawdzając duble pod kątem konkretnych pól?

np. MójObiekt posiada pola: id, a, b, c, d, e, f

Posiadam listę z np 500 000 el. i chciałbym z niej usunąć elementy o tych samych wartościach A, C, F.

W jaki sposób mogę tego dokonać w najkrótszym czasie?

Pozdrawiam

JU
  • Rejestracja:około 22 lata
  • Ostatnio:około miesiąc
  • Postów:5042
0

Możesz spróbować tego:
http://stackoverflow.com/questions/30366669/most-efficient-way-to-remove-duplicates-from-a-list

To się pewnie wiąże z przesłonięciem takich metod jak equals, getHashCode, czy operator==. Ale chyba nie ma innej drogi.
Mógłbyś też zwracać unikalne hashe dla konkretnych elementów. I wtedy albo dictionary, gdzie kluczem jest hash - bardzo szybko wtedy nie pozwolisz na dodanie duplikatu, albo po prostu będziesz porównywał po hashu.

Tylko, jeśli zapisujesz tą listę do pliku, czy do bazy, to musisz pamiętać, żeby nie zapisywać hasha, tylko tworzyć go zawsze po wczytaniu listy. Chyba, że zrobisz własny mechanizm dla hashy i on się nigdy nie zmieni.

DR
  • Rejestracja:ponad 13 lat
  • Ostatnio:prawie 4 lata
  • Postów:37
0

W jaki sposób mogę zwracać unikalne hashe biorąc pod uwagę to że pozostałe parametry mogą być różne?

somekind
Moderator
  • Rejestracja:około 17 lat
  • Ostatnio:2 dni
  • Lokalizacja:Wrocław
0

Możesz przeciążyć GetHashCode tak, aby brała tylko interesujące Cie wartości pod uwagę.
A co jest źródłem danych w tej liście?

DR
  • Rejestracja:ponad 13 lat
  • Ostatnio:prawie 4 lata
  • Postów:37
0

Dane wyciągane są z bazy.

somekind
Moderator
  • Rejestracja:około 17 lat
  • Ostatnio:2 dni
  • Lokalizacja:Wrocław
0

To może zamiast je usuwać z listy, po prostu nie pobieraj tych zbędnych?

DR
  • Rejestracja:ponad 13 lat
  • Ostatnio:prawie 4 lata
  • Postów:37
0

Ale ja je potrzebuje tylko po jednej sztuce

E9
Koledze wyżej chodziło żeby to baza już zajęła się odrzuceniem duplikatów. Więc może musisz napisać jakieś zapytanie/procedurę SQL? W większości przypadków taka operacja będzie wydajniejsza niż dzielenie tego po stronie kodu.
somekind
Moderator
  • Rejestracja:około 17 lat
  • Ostatnio:2 dni
  • Lokalizacja:Wrocław
0

Jeśli potrzebujesz jednej sztuki, to po co Ci lista?
Sprecyzuj co chcesz osiągnąć.

DR
  • Rejestracja:ponad 13 lat
  • Ostatnio:prawie 4 lata
  • Postów:37
0

Nie zrozumieliśmy się :)
Mam listę typu MojaKlasa która posiada np. 500 000 elementów
Klasa posiada pola A, B, C, D

100 000 elementów z tej listy ma taką samą wartość B,C,D, ale różną wartość A, dlatego distinct sobie z tym nie poradzi, chce odfiltrować te duplikaty i zostawić tylko jeden obiekt z tych 100 000.
Powiedzmy, że pozostałe elementy też mają taką zależność więc w wyniku końcowym nadal mieć listę typu MojaKlasa która zwróci wg opisanego przykładu 5 obiektów.

Aktualnie próbuję zaimplementować IEqualityComparer jednak nie wiem jak obsłużyć GetHashCode w przypadku jeśli pole może być nullem (long?) oraz jeśli jest obiektem który może być np. tablicą.

neves
  • Rejestracja:prawie 22 lata
  • Ostatnio:około 9 godzin
  • Lokalizacja:Kraków
  • Postów:1114
1

Odnośnie tego jak liczyć hasha na podstawie już istniejących hashy z naszych pól, to właśnie się natknął na to na stackoverflow:
http://stackoverflow.com/questions/263400/what-is-the-best-algorithm-for-an-overridden-system-object-gethashcode/263416#263416


DR
Po co to 17 i mnożenie razy 23?
T9
po to żeby zmniejszyć prawdopodobieństwo pojawienia się takich samych haszy dla 2 obiektów, powiedzmy że masz dwa biekty o1={a=2,b=1} , o2={a=3,b=0}, gdbyś liczył hashe jako a+b to dla obu obiektów będą miały wartość 3, ale liczą np. h=1;h=h10+a; h=h10+b; otrzymasz 121 i 130. Oczywiście 10 jest kiepską liczbą bo bez problemu można sobie wyobrazić dane dla których wystąpi kolizja, dlatego zamiast okrągłych mnoży się przez liczby pierwsze które dają bardzie zagmatwane wyniki.
somekind
Moderator
  • Rejestracja:około 17 lat
  • Ostatnio:2 dni
  • Lokalizacja:Wrocław
2

@drakoo, a ja Ci mówię, żeby zamiast pobierać z bazy 500 tys elementów pobrał tylko 400 tys (czy ile Ci tam trzeba).

DR
  • Rejestracja:ponad 13 lat
  • Ostatnio:prawie 4 lata
  • Postów:37
0

Niestety odsianie po stronie bazy odpada, poza tym zapytanie które by odsiewało takie duble trwało by znacznie dłużej niż zrobienie tego po stronie c#.
Zaimplementowałem taką metode:

Kopiuj
        public int GetDataHashCode(MyClass obj)
        {
            long idLoc = obj.IdLoc.HasValue ? obj.IdLoc.Value : 0;
            long idMet = obj.IdMet.HasValue ? obj.IdMet.Value : 0;
            long ser = obj.SerHasValue ? obj.Ser.Value : 0;
            int valueHash = 0;
            if (obj.Value != null)
                valueHash = obj.Value.GetHashCode();
            else
                valueHash = valueHash.GetHashCode();

            return (idLoc.GetHashCode() + idMet.GetHashCode() + ser.GetHashCode() + valueHash  + obj.IdDataType.GetHashCode() + obj.Time.GetHashCode()).GetHashCode();
        }

Póki co kręcę się foreachem po danych wyznaczam hashcode i wkładam do słownika. Przetworzenie w taki sposób 750 000 elementów zajmuje około 0,2 sekundy!

edytowany 1x, ostatnio: drakoo
neves
  • Rejestracja:prawie 22 lata
  • Ostatnio:około 9 godzin
  • Lokalizacja:Kraków
  • Postów:1114
2

Nie zapomnij tylko po tym jak hashe się zgodzą porównywać obiektów czy rzeczywiście są takie same pole po polu, bo mówią delikatnie przy takiej implementacji możesz mieć sporo kolizji.


1

Skad wniosek, ze odsianie po stronie bazy zajmie sluzej niz jakims foreachem?

DR
  • Rejestracja:ponad 13 lat
  • Ostatnio:prawie 4 lata
  • Postów:37
0
neves napisał(a):

Nie zapomnij tylko po tym jak hashe się zgodzą porównywać obiektów czy rzeczywiście są takie same pole po polu, bo mówią delikatnie przy takiej implementacji możesz mieć sporo kolizji.

Sam hash nie zapewni mi że na pewno są takie same?
Czyli co lepiej zaimplementować IEqualityComparer który zawiera gethash oraz equals, problem w tym że nie wiem jak w Equals porównywać typ Object

edytowany 1x, ostatnio: drakoo
JU
  • Rejestracja:około 22 lata
  • Ostatnio:około miesiąc
  • Postów:5042
2

Hash Ci jednoznacznie zidentyfikuje obiekt, jeśli go tak napiszesz. Jeśli np. Twoja metoda GetHashCode będzie wyglądała tak:

Kopiuj
 return a + b + c + d;

To na pewno nie będziesz miał unikalnych haszy.

Jeśli używasz w jakiś sposób domyślnej metody, musisz uważać na to, co pisze MS:
Two objects that are equal return hash codes that are equal. However, the reverse is not true: equal hash codes do not imply object equality, because different (unequal) objects can have identical hash codes. Furthermore, the .NET Framework does not guarantee the default implementation of the GetHashCode method, and the value this method returns may differ between .NET Framework versions and platforms, such as 32-bit and 64-bit platforms. For these reasons, do not use the default implementation of this method as a unique object identifier for hashing purposes

(https://msdn.microsoft.com/pl-pl/library/system.object.gethashcode(v=vs.110).aspx)
W prawdzie nie spotkałem się z tym, żeby różne obiekty dały ten sam hash code, ale to jest możliwe. Chociaż jestem ciekawy, czy ktoś wie, jaka jest szansa na to?

JU
Czyli nawet przy miliardzie rekordów można założyć, że hash będzie unikalny?
krzysiek050
Założyć że hash będzie unikalny możesz tylko dla 1 rekrodu. Dla 2 już musisz założyć że może się zdarzyć duplikat.
DR
Ale mi właśnie chodzi o to żeby wyłapać duplikaty, wiec dlaczego robiąc return (a.GetHashCode() + b.GetHashCode()).GetHashCode, moze wystąpić kolizja? W takiej implementacji hashcode powinien być taki sam dla obiektów z tymi samymi parametrami a o to mi chodzi, wiec po co mam jeszcze equalsem porównywać ?
krzysiek050
Nie porównujesz equalsem. Zasada jest taka jak przytoczył @Juhas żeby później nie szukać bardzo trudnych do znalezienia błędów. W typ wypadku equals nie zostanie użyty, ale być może inny programista coś dopisze i będzie miał przez to problem. Dlatego jeżeli potrzebujesz tylko hashcode lub tylko equals, to zawsze nadpisujesz obie.
somekind
Moderator
  • Rejestracja:około 17 lat
  • Ostatnio:2 dni
  • Lokalizacja:Wrocław
1
Juhas napisał(a):

Chociaż jestem ciekawy, czy ktoś wie, jaka jest szansa na to?

Policz sobie na podstawie wielkości zbioru obiektów oraz wielkości zbioru możliwych wartości zwracanych z GetHashCode (czyli int.MaxValue).

Aventus
  • Rejestracja:około 9 lat
  • Ostatnio:ponad 2 lata
  • Lokalizacja:UK
  • Postów:2235
4

Takie filtrowanie powinno się odbyć na etapie wyciągania danych z bazy.


Na każdy złożony problem istnieje rozwiązanie które jest proste, szybkie i błędne.
DR
  • Rejestracja:ponad 13 lat
  • Ostatnio:prawie 4 lata
  • Postów:37
0
neves napisał(a):

Nie zapomnij tylko po tym jak hashe się zgodzą porównywać obiektów czy rzeczywiście są takie same pole po polu, bo mówią delikatnie przy takiej implementacji możesz mieć sporo kolizji.

Czyli w końcu jak mam się do tego odnieść?

Kopiuj
 class Comparer : IEqualityComparer<MyClass>
        {
            public bool Equals(MyClass x, MyClass y)
            {
                int xValue = x.Value.GetHashCode();
                int yValue = y.Value.GetHashCode();
                return x.IdLoc == y.IdLoc && x.IdMet == y.IdMet && x.Ser == y.Ser &&
                    x.IdDataType == y.IdDataType && x.Time == y.Time && xValue == yValue;
            }

            public int GetHashCode(MyClass obj)
            {
                long idLoc = obj.IdLoc.HasValue ? obj.IdLoc.Value : 0;
                long idMet = obj.IdMet.HasValue ? obj.IdMet.Value : 0;
                long ser = obj.Ser.HasValue ? obj.Ser.Value : 0;
                int valueHash = 0;
                if (obj.Value != null)
                    valueHash = obj.Value.GetHashCode();
                else
                    valueHash = valueHash.GetHashCode();

                return (idLoc.GetHashCode() + idMet.GetHashCode() + ser.GetHashCode() + valueHash + obj.IdDataType.GetHashCode() + obj.Time.GetHashCode()).GetHashCode();
            }
        }

W ten sposób lepiej?

neves
  • Rejestracja:prawie 22 lata
  • Ostatnio:około 9 godzin
  • Lokalizacja:Kraków
  • Postów:1114
1

Lepiej, nie jest to najbardziej optymalny sposób (sposób liczenia hasha, i używania hashy w bezpośrednim porównywaniu), no ale jeśli nie zrobiłeś błędu w metodzie porównującej (czego nie da się stwierdzić bez wiedzy jak wygląda obiekt i co decyduje o jego unikalności od innych), to powinno śmigać :).


somekind
najbardziej optymalny? A cóż to znaczy?
neves
jest to wyrażenie używane w mowie potocznej, które z punktu widzenia formalnego/poprawnościowego nie ma sensu, ale mimo wszystko doskonale wiesz co autor miał na myśli ;P
DR
odnosnie linku z stackoverflow "jak liczyć hasha na podstawie już istniejących hashy z naszych pól" po co tam jest to 17 i mnożenie razy 23? coś to zmienia gdyby to pominąć?
neves
zmienia, jest to stosowane by osiągnąć lepszy rozkład i zmniejszyć ryzyko kolizji, czyli zmniejszyć liczbę wywołań metody Equals
DR
  • Rejestracja:ponad 13 lat
  • Ostatnio:prawie 4 lata
  • Postów:37
0

Mam problem z: valueHash = obj.Value.GetHashCode();

Value to zmienna typu Object w ktorej mam tablice bajtów byte[7] z takimi samymi wartościami, jednak za każdym razem jest dla niej generowany inny hashcode, jak mogę sobie z tym poradzić?

neves
  • Rejestracja:prawie 22 lata
  • Ostatnio:około 9 godzin
  • Lokalizacja:Kraków
  • Postów:1114
1

Tablica jest typem referencyjnym, więc domyślnie zarówno GetHashCode jaki i Equals porównuje referencje, musisz zrobić to samo co dla obiektu, czyli napisać własną implementację aby porównywać zawartość, tak jak tutaj chociażby:
http://stackoverflow.com/questions/7244699/gethashcode-on-byte-array


DR
Tak, właśnie to sprawdzam plus jakieś inne sposoby, tylko po dodaniu właśnie tego mnożenia *23 oraz zastosowaniu ArrayEqualityComparer, zaczeło mi wykonywać Equals, w którym znowy mam problem z porównywaniem obiektów...
DR
W przypadku jeśli w object będę miał string, int, double robienie gethashcode na obiekcie zawsze zwróci tą samą wartość?
neves
dla obiektu typu referencyjnego nie ma znaczenia co masz w środku, zarówno gethashcode i equals jest domyślnie liczone na podstawie referencji, dlatego musimy dostarczyć własne implementacje które biorą pod uwagę wartości trzymane w środku obiektu
Kliknij, aby dodać treść...

Pomoc 1.18.8

Typografia

Edytor obsługuje składnie Markdown, w której pojedynczy akcent *kursywa* oraz _kursywa_ to pochylenie. Z kolei podwójny akcent **pogrubienie** oraz __pogrubienie__ to pogrubienie. Dodanie znaczników ~~strike~~ to przekreślenie.

Możesz dodać formatowanie komendami , , oraz .

Ponieważ dekoracja podkreślenia jest przeznaczona na linki, markdown nie zawiera specjalnej składni dla podkreślenia. Dlatego by dodać podkreślenie, użyj <u>underline</u>.

Komendy formatujące reagują na skróty klawiszowe: Ctrl+B, Ctrl+I, Ctrl+U oraz Ctrl+S.

Linki

By dodać link w edytorze użyj komendy lub użyj składni [title](link). URL umieszczony w linku lub nawet URL umieszczony bezpośrednio w tekście będzie aktywny i klikalny.

Jeżeli chcesz, możesz samodzielnie dodać link: <a href="link">title</a>.

Wewnętrzne odnośniki

Możesz umieścić odnośnik do wewnętrznej podstrony, używając następującej składni: [[Delphi/Kompendium]] lub [[Delphi/Kompendium|kliknij, aby przejść do kompendium]]. Odnośniki mogą prowadzić do Forum 4programmers.net lub np. do Kompendium.

Wspomnienia użytkowników

By wspomnieć użytkownika forum, wpisz w formularzu znak @. Zobaczysz okienko samouzupełniające nazwy użytkowników. Samouzupełnienie dobierze odpowiedni format wspomnienia, zależnie od tego czy w nazwie użytkownika znajduje się spacja.

Znaczniki HTML

Dozwolone jest używanie niektórych znaczników HTML: <a>, <b>, <i>, <kbd>, <del>, <strong>, <dfn>, <pre>, <blockquote>, <hr/>, <sub>, <sup> oraz <img/>.

Skróty klawiszowe

Dodaj kombinację klawiszy komendą notacji klawiszy lub skrótem klawiszowym Alt+K.

Reprezentuj kombinacje klawiszowe używając taga <kbd>. Oddziel od siebie klawisze znakiem plus, np <kbd>Alt+Tab</kbd>.

Indeks górny oraz dolny

Przykład: wpisując H<sub>2</sub>O i m<sup>2</sup> otrzymasz: H2O i m2.

Składnia Tex

By precyzyjnie wyrazić działanie matematyczne, użyj składni Tex.

<tex>arcctg(x) = argtan(\frac{1}{x}) = arcsin(\frac{1}{\sqrt{1+x^2}})</tex>

Kod źródłowy

Krótkie fragmenty kodu

Wszelkie jednolinijkowe instrukcje języka programowania powinny być zawarte pomiędzy obróconymi apostrofami: `kod instrukcji` lub ``console.log(`string`);``.

Kod wielolinijkowy

Dodaj fragment kodu komendą . Fragmenty kodu zajmujące całą lub więcej linijek powinny być umieszczone w wielolinijkowym fragmencie kodu. Znaczniki ``` lub ~~~ umożliwiają kolorowanie różnych języków programowania. Możemy nadać nazwę języka programowania używając auto-uzupełnienia, kod został pokolorowany używając konkretnych ustawień kolorowania składni:

```javascript
document.write('Hello World');
```

Możesz zaznaczyć również już wklejony kod w edytorze, i użyć komendy  by zamienić go w kod. Użyj kombinacji Ctrl+`, by dodać fragment kodu bez oznaczników języka.

Tabelki

Dodaj przykładową tabelkę używając komendy . Przykładowa tabelka składa się z dwóch kolumn, nagłówka i jednego wiersza.

Wygeneruj tabelkę na podstawie szablonu. Oddziel komórki separatorem ; lub |, a następnie zaznacz szablonu.

nazwisko;dziedzina;odkrycie
Pitagoras;mathematics;Pythagorean Theorem
Albert Einstein;physics;General Relativity
Marie Curie, Pierre Curie;chemistry;Radium, Polonium

Użyj komendy by zamienić zaznaczony szablon na tabelkę Markdown.

Lista uporządkowana i nieuporządkowana

Możliwe jest tworzenie listy numerowanych oraz wypunktowanych. Wystarczy, że pierwszym znakiem linii będzie * lub - dla listy nieuporządkowanej oraz 1. dla listy uporządkowanej.

Użyj komendy by dodać listę uporządkowaną.

1. Lista numerowana
2. Lista numerowana

Użyj komendy by dodać listę nieuporządkowaną.

* Lista wypunktowana
* Lista wypunktowana
** Lista wypunktowana (drugi poziom)

Składnia Markdown

Edytor obsługuje składnię Markdown, która składa się ze znaków specjalnych. Dostępne komendy, jak formatowanie , dodanie tabelki lub fragmentu kodu są w pewnym sensie świadome otaczającej jej składni, i postarają się unikać uszkodzenia jej.

Dla przykładu, używając tylko dostępnych komend, nie możemy dodać formatowania pogrubienia do kodu wielolinijkowego, albo dodać listy do tabelki - mogłoby to doprowadzić do uszkodzenia składni.

W pewnych odosobnionych przypadkach brak nowej linii przed elementami markdown również mógłby uszkodzić składnie, dlatego edytor dodaje brakujące nowe linie. Dla przykładu, dodanie formatowania pochylenia zaraz po tabelce, mogłoby zostać błędne zinterpretowane, więc edytor doda oddzielającą nową linię pomiędzy tabelką, a pochyleniem.

Skróty klawiszowe

Skróty formatujące, kiedy w edytorze znajduje się pojedynczy kursor, wstawiają sformatowany tekst przykładowy. Jeśli w edytorze znajduje się zaznaczenie (słowo, linijka, paragraf), wtedy zaznaczenie zostaje sformatowane.

  • Ctrl+B - dodaj pogrubienie lub pogrub zaznaczenie
  • Ctrl+I - dodaj pochylenie lub pochyl zaznaczenie
  • Ctrl+U - dodaj podkreślenie lub podkreśl zaznaczenie
  • Ctrl+S - dodaj przekreślenie lub przekreśl zaznaczenie

Notacja Klawiszy

  • Alt+K - dodaj notację klawiszy

Fragment kodu bez oznacznika

  • Alt+C - dodaj pusty fragment kodu

Skróty operujące na kodzie i linijkach:

  • Alt+L - zaznaczenie całej linii
  • Alt+, Alt+ - przeniesienie linijki w której znajduje się kursor w górę/dół.
  • Tab/⌘+] - dodaj wcięcie (wcięcie w prawo)
  • Shit+Tab/⌘+[ - usunięcie wcięcia (wycięcie w lewo)

Dodawanie postów:

  • Ctrl+Enter - dodaj post
  • ⌘+Enter - dodaj post (MacOS)