NOVINKA - Online rekvalifikační kurz Java programátor. Oblíbená a studenty ověřená rekvalifikace - nyní i online.
NOVINKA – Víkendový online kurz Software tester, který tě posune dál. Zjisti, jak na to!
Avatar
beats.omni
Člen
Avatar
beats.omni:22.10.2015 17:12

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ý
Tvůrce
Avatar
Odpovídá na beats.omni
Jan Vargovský:22.10.2015 17:28

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í
+2,50 Kč
Řešení problému
 
Nahoru Odpovědět
22.10.2015 17:28
Avatar
Milan Křepelka
Tvůrce
Avatar
Milan Křepelka:22.10.2015 18:01

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/…vs.110).aspx

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

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ý
Tvůrce
Avatar
Odpovídá na beats.omni
Jan Vargovský:23.10.2015 19:09

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
Tvůrce
Avatar
Odpovídá na Jan Vargovský
Milan Křepelka:24.10.2015 14:02

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

 
Nahoru Odpovědět
24.10.2015 14:02
Avatar
Jan Vargovský
Tvůrce
Avatar
Odpovídá na Milan Křepelka
Jan Vargovský:24.10.2015 18:48

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
Tvůrce
Avatar
Odpovídá na Jan Vargovský
Milan Křepelka:25.10.2015 7:04

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:17.12.2015 15:22

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
Odpovídá na Milan Křepelka
Marian Benčat:18.12.2015 3:01

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
Totalitní admini..
Avatar
Milan Křepelka
Tvůrce
Avatar
Milan Křepelka:18.12.2015 5:39

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

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

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:19.12.2015 21:59

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:19.12.2015 22:01

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.