Avatar
beats.omni
Člen
Avatar
beats.omni:

Ahoj, mám dva listy vlastního datového typu a když je chci porovnat, tak mi to nefunguje, jak bych si představoval :-) V prvním listu mám tři lidi. Ve druhém pouze dva. Oba dva lidé ve druhém listu jsou stejní, jako lidé v prvním listu. A já se teď snažím vypsat pouze ty, kteří chybí. Se současným kódem mi to ale pokaždé vypíše všechny lidi. Kde dělám chybu?

List<Clovek> clovek_01 = new List<Clovek>();
List<Clovek> clovek_02 = new List<Clovek>();

// Pro list clovek_01
clovek_01.Add(new Clovek()
{
    Jmeno = "Pavel",
    Prijmeni = "Novak",
    Vek = 20
});

clovek_01.Add(new Clovek()
{
    Jmeno = "Marek",
    Prijmeni = "Novak",
    Vek = 30
});

clovek_01.Add(new Clovek()
{
    Jmeno = "Vlasta",
    Prijmeni = "Novak",
    Vek = 40
});

// Pro list clovek_02
clovek_02.Add(new Clovek()
{
    Jmeno = "Pavel",
    Prijmeni = "Novak",
    Vek = 20
});

clovek_02.Add(new Clovek()
{
    Jmeno = "Marek",
    Prijmeni = "Novak",
    Vek = 30
});

foreach (var item in clovek_01)
{
    if (!clovek_02.Contains(item))
    {
        Console.WriteLine($"{item.Jmeno} {item.Prijmeni}");
    }
}
Editováno 22.10.2015 17:14
 
Odpovědět 22.10.2015 17:12
Avatar
Jan Vargovský
Redaktor
Avatar
Odpovídá na beats.omni
Jan Vargovský:

Vždyť to funguje... Přepsal sis Equals? Aspoň nějak tahkle?

public override bool Equals(object obj)
{
    var clovek = (Clovek)obj;

    if (clovek == null)
        return false;

    return (Jmeno.Equals(clovek.Jmeno) &&
        Prijmeni.Equals(clovek.Prijmeni) &&
        Vek.Equals(clovek.Vek));
}

Popřípadě můžeš použít komparátor a extension metody:

    class MyClass : IEqualityComparer<Clovek>
    {
        public bool Equals(Clovek x, Clovek y)
        {
            return (x.Jmeno.Equals(y.Jmeno) &&
                x.Prijmeni.Equals(y.Prijmeni) &&
                x.Vek.Equals(y.Vek));
        }

        public int GetHashCode(Clovek obj)
        {
            return ((obj.Jmeno.GetHashCode() & obj.Prijmeni.GetHashCode()) ^ obj.Vek) * 19;
        }
    }

foreach (var item in clovek_01.Except(clovek_02, new MyClass()))
{
    Console.WriteLine($"{item.Jmeno} {item.Prijmeni}");
}
Akceptované řešení
+20 Zkušeností
+1 bodů
Řešení problému
 
Nahoru Odpovědět 22.10.2015 17:28
Avatar
Milan Křepelka
Redaktor
Avatar
Milan Křepelka:

Já k Honzovi přidám trochu omáčky. To kouzlo je v porovnávání referenčních typů. Ten porovnávací stroj ve výchozím stavu porovnáva odkazy v paměti. Tedy

var clovek_1 = new Clovek()
{
    Jmeno = "Marek",
    Prijmeni = "Novak",
    Vek = 30
}

a toto

var clovek_2 = new Clovek()
{
    Jmeno = "Marek",
    Prijmeni = "Novak",
    Vek = 30
}

Jsou dva různé objekty protože sedí na různých adresách v paměti. Aby to fungovalo jak jsi predpokládal je potřeba tomu porovnávacímu stroji dodat nový "předpis" či "klíč" podle kterého má porovnávat. Honza ti popsal jeden způsob. Kdyby ses kouknul na dokumentaci ke Contains, tak tam to je taktéž popsané.

https://msdn.microsoft.com/…ary/bhkz42b3(v=vs.110).aspx

 
Nahoru Odpovědět 22.10.2015 18:01
Avatar
beats.omni
Člen
Avatar
Odpovídá na Jan Vargovský
beats.omni:

Ahoj, ještě bych se chtěl zeptat, proč se musí přepisovat GetHashCode() a proč zrovna takto?

return ((obj.Jmeno.GetHashCode() & obj.Prijmeni.GetHashCode()) ^ obj.Vek) * 19;

Díky za případné vysvětlení.

 
Nahoru Odpovědět 23.10.2015 12:36
Avatar
Jan Vargovský
Redaktor
Avatar
Odpovídá na beats.omni
Jan Vargovský:

Protože je rychlejší spočítat hash a pak v případě shody ověřit jestli jsou fakt ty objekty shodne.

 
Nahoru Odpovědět 23.10.2015 19:09
Avatar
Milan Křepelka
Redaktor
Avatar
Odpovídá na Jan Vargovský
Milan Křepelka:

Tak to řekněmě že spíše ne.

 
Nahoru Odpovědět 24.10.2015 14:02
Avatar
Jan Vargovský
Redaktor
Avatar
Odpovídá na Milan Křepelka
Jan Vargovský:

Tak mi řekni jiný důvod, proč bys měl mít u každého objektu GetHashCode(). Jsem zvědav.

 
Nahoru Odpovědět 24.10.2015 18:48
Avatar
Milan Křepelka
Redaktor
Avatar
Odpovídá na Jan Vargovský
Milan Křepelka:

To s tím nesouvisí. Já jenom říkám, že počítání hashe je poměrně náročná činnost oproti porovnání.

 
Nahoru Odpovědět 25.10.2015 7:04
Avatar
beats.omni
Člen
Avatar
Odpovídá na Jan Vargovský
beats.omni:

Ahoj, ještě dotaz, jak to ošetřím, když bude některá hodnota null (třeba Jmeno nebo Vek) ?

Editováno 17.12.2015 15:22
 
Nahoru Odpovědět 17.12.2015 15:22
Avatar
Marian Benčat
Redaktor
Avatar
Odpovídá na Milan Křepelka
Marian Benčat:

On má svý, způsobem pravdu.. GetHashCode použivají některé kolekce (například dictionary) aby si mohli "pobalíčkovat" objekty a tak zrychlit hledání.

To že je počítání hashe náročné je nesmysl. Vždy záleží na implementaci. Ten hash si můžu klidně dát konstantní a bude to v O(1).

Pokud mám dva objekty které jsou téměř stejné - liší se třeba jen v jedné propertě, budou mít na základě "nepřesné" GetHashCode metody stejnou hodnotu.. POkud tedy tento objekt umístím do Dictionary mezi 1000000 zcela rozdílných objektů a potom se zeptám, jestli se tam nachází "ten druhý" objekt. On si spošte HashCode a pak se koukne u sebe "do přihrádky", kde má jen instanc s tímto HashCodem.. v našem případě je třeba pouze 1.. Nemusí tedy prohledávat více objektů, ale jen mezi těmi co mají stejný HashCode.

 
Nahoru Odpovědět 18.12.2015 3:01
Avatar
Milan Křepelka
Redaktor
Avatar
Milan Křepelka:

Je potřeba číst více slov ... nejenom jedno

 
Nahoru Odpovědět 18.12.2015 5:39
Avatar
abushrek
Člen
Avatar
abushrek:

Tady je kód pro výpis těch kteří nejsou v kolekci clovek1:

foreach(Clovek clovek in clovek1.Where(s=>clovek2.forEach(b=>b != s)).Select(s=>s))
{
        Console.WriteLine(+"jmeno: "+clovek.Jmeno+"\nprijimeni: "+clovek.Prijimeni+"\nvek: "+clovek.Vek);
}

Pokud chceš porovnávat jména stačí udělat toto:

foreach(Clovek clovek in clovek1.Where(s=>clovek2.forEach(b=>b.Jmeno != s.Jmeno)).Select(s=>s))
{
        Console.WriteLine(+"jmeno: "+clovek.Jmeno+"\nprijimeni: "+clovek.Prijimeni+"\nvek: "+clovek.Vek);
}
 
Nahoru Odpovědět 19.12.2015 21:51
Avatar
abushrek
Člen
Avatar
Odpovídá na abushrek
abushrek:

To nadtím je špatně...

foreach (Clovek clovek in clovek1.Where(s=>!clovek2.Contains(s)))
            {
                Console.WriteLine(+"jmeno: " + clovek.Jmeno + "\nprijimeni: " + clovek.Prijimeni + "\nvek: " + clovek.Vek);
            }
 
Nahoru Odpovědět 19.12.2015 21:59
Avatar
abushrek
Člen
Avatar
Odpovídá na abushrek
abushrek:

A pokud chceš porovnávat třeba jména tak

foreach (Clovek clovek in clovek1.Where(s=>!clovek2.Any(d=>d.Jmeno==s.Jmeno)))
            {
                Console.WriteLine(+"jmeno: " + clovek.Jmeno + "\nprijimeni: " + clovek.Prijimeni + "\nvek: " + clovek.Vek);
            }
 
Nahoru Odpovědět 19.12.2015 22:01
Děláme co je v našich silách, aby byly zdejší diskuze co nejkvalitnější. Proto do nich také mohou přispívat pouze registrovaní členové. Pro zapojení do diskuze se přihlas. Pokud ještě nemáš účet, zaregistruj se, je to zdarma.

Zobrazeno 14 zpráv z 14.