Diskuze: C# IComparable, Algoritmus porovnávání

C# .NET .NET (C# a Visual Basic) C# IComparable, Algoritmus porovnávání American English version English version

Avatar
TonySensu
Člen
Avatar
TonySensu:

Ahoj jsem začátečník v C# a mám za úkol vytvořit algoritmus porovnávání kódů které vypadají nějak takto:

List<Ciselnik2> kody2 = new List<Ciselnik2> {
                new Ciselnik2("13.1"),
                new Ciselnik2("13.10"),
                new Ciselnik2("13.2"),
                new Ciselnik2("13.11"),
                new Ciselnik2("ahoj")};

Výsledkem má být seřazení tohoto listu do této podoby: 13.1, 13,2, 13.10, 13.11
kody2.Sort() seřazuje špatně - 13.1, 13.10, 13.11, 13.2...
Mám implementované rozhraní IComparable ale nevím jak na to, aby to seřazovalo tak jak chci.
Postup by měl být takový:
1).pokud některá z instancí není číselník tak vyhodit vyjimku

  1. z obou instancí si vytáhnout Kód a ten rozsekat na části podle separátoru (.)
  2. postupně každou odpovídající část z obou kódů porovnat mezi sebou. Před porovnáním je nutno řetězce srovnat na stejnou délku např. pomocí PadLeft()
  3. jakmile se některé části mezi sebou nerovnají, ukončujeme porovnání
  4. pokud dojdeme sem, jedná se o dva identické kódy

výše popsaný algoritmus by měl být aplikovatelný i na nečíselné kódy, tzn. porovná i "blablabla" : "10.2"

Takže "teorii" k tomu mám jen nevím jak to v tom C# napsat :D S C# jsem začat teprve nedávno, ale nějaké základy a OOP už jsem si pročetl tady na IT networku i jinde, ale i tak jsem na tohle zatím nepřišel.

 
Odpovědět 22. srpna 16:20
Avatar
Lukas C#
Redaktor
Avatar
Lukas C#:

Jestli máš implementované rozhraní IComparable, tak sem postni tvojí verzi metody Compare (tzn. cos zatím zkoušel). Podíváme se na to.

 
Nahoru Odpovědět 22. srpna 17:40
Avatar
ostrozan
Redaktor
Avatar
ostrozan:

Taky by mně zajímala ta třída "Ciselnik2"

 
Nahoru Odpovědět 22. srpna 19:14
Avatar
TonySensu
Člen
Avatar
Odpovídá na ostrozan
TonySensu:
class Ciselnik2 : IComparable
   {
       public string Kod { get; set; }
       public int DalsiAtribut { get; set; }
       static Random rand = new Random();
       public Ciselnik2(string kod)
       {
           this.Kod = kod;
           DalsiAtribut = rand.Next(10,20);
       }
       public int CompareTo(object obj)
       {
           if (obj == null) return 1;

           Ciselnik2 otherCiselnik = obj as Ciselnik2;
           if (otherCiselnik != null)
           {
               /*
               string[] s = this.Kod.Split('.');
               string[] sOther = otherCiselnik.Kod.Split('.');


                   s[i].PadLeft(2, '0');
                   sOther[i].PadLeft(2, '0');
             */


               return this.Kod.CompareTo(otherCiselnik.Kod);
           }
           else
               throw new ArgumentException("Object is not Ciselnik2");
       }

   }

Ta třída je jen pro zkušební účely..až to bude fungovat tak to pak aplikuji na projekt do třídy, která má další vlastnosti atd.. ale to už by snad nemělo vadit.

 
Nahoru Odpovědět 23. srpna 8:06
Avatar
TonySensu
Člen
Avatar
Odpovídá na Lukas C#
TonySensu:
public int CompareTo(object obj)
     {
         if (obj == null) return 1;

         Ciselnik2 otherCiselnik = obj as Ciselnik2;
         if (otherCiselnik != null)
         {
             /*
             string[] s = this.Kod.Split('.');
             string[] sOther = otherCiselnik.Kod.Split('.');


                 s[i].PadLeft(2, '0');
                 sOther[i].PadLeft(2, '0');
           */


             return this.Kod.CompareTo(otherCiselnik.Kod);
         }
         else
             throw new ArgumentException("Object is not Ciselnik2");
     }

V podstatě je to jen něco, co jsem našel v oficialni dokumentaci od MS (kterou nesnašim :D) a nějaky pokus, který jsem zakomentoval - netuším, jak to tam správně zakomponovat.

Editováno 23. srpna 8:10
 
Nahoru Odpovědět 23. srpna 8:09
Avatar
ostrozan
Redaktor
Avatar
Odpovídá na TonySensu
ostrozan:

A je nutný ten formát"13.1 , 13.10 ......" nebylo by lepší něco co půjde líp seřadit "1301,1310......" než vymýšlet nějaké krkolomnosti?
Ta třída mně zajímala kvůli tomu, jestli umí i něco jiného, než uchovat nějaký kód. Takhle se zdá zbytečná a vystačíš si s kolekcí stringů

 
Nahoru Odpovědět 23. srpna 8:31
Avatar
TonySensu
Člen
Avatar
Odpovídá na ostrozan
TonySensu:

Bohužel formát je potřeba takový je to nějaký kód...Jasný takto na test by stačila kolekce stringů, ale potom v druhém projektu to chci aplikovat na třídu, která umí i další věci.

 
Nahoru Odpovědět 23. srpna 8:33
Avatar
ostrozan
Redaktor
Avatar
ostrozan:

Tak si to do toho formátu uprav - stejně ses tou cestou vydal, ale já bych to dal už do té property

private string code;
public string Kod {
    get { return code; }
    set
    {
        string s = value;
        string[] s2 = s.Split('.');
        code = s2[0].PadLeft(2, '0') + s2[1].PadLeft(2, '0');

    }

}

a analogicky i do konstruktoru

Editováno 23. srpna 9:32
 
Nahoru Odpovědět 23. srpna 9:31
Avatar
TonySensu
Člen
Avatar
Odpovídá na ostrozan
TonySensu:

Ve výsledku žádný rozdíl. Jak to myslíš v tom konstruktoru?

 
Nahoru Odpovědět 23. srpna 9:49
Avatar
TonySensu
Člen
Avatar
Odpovídá na ostrozan
TonySensu:

Takhle jsi to sice přeformátoval už při nastavení té property, ale stejně při porovnávání to porovnává celý řetězec, takže asi kvůli tomu si myslím je ten výsledek stejný. Ono by to asi chtělo nějak udělat aby to porovnávalo každej part toho kódu zvlášt s druhým kódem a jeho částí, která odpovída té části u prvního kódu.

 
Nahoru Odpovědět 23. srpna 10:01
Avatar
TonySensu
Člen
Avatar
TonySensu:

Navíc ve výstupu chci, aby kody vypadali stejně, tzn: 13.1 a ne 13.01.

 
Nahoru Odpovědět 23. srpna 10:05
Avatar
TonySensu
Člen
Avatar
Odpovídá na ostrozan
TonySensu:

Tak už se mi to podařilo částečně rozjet, ale vyhodí to exception, když tam přidám třeba to "ahoj" asi kvůli tomu

(s[0].PadLeft(2, '0')) + (s[1].PadLeft(2, '0'))

Tady se počítá s tím, že ten kód bude mít vždycky 2 části..nejde to nějak upravit, aby to bylo univerzální? Aby to porovnalo "ahoj", ale i třeba 13.10.5 atd..

 
Nahoru Odpovědět 23. srpna 10:35
Avatar
TonySensu
Člen
Avatar
TonySensu:

Napadlo mě, že bych si mohl zjistit délku pole toho stringu a pak udělat for cyklus na ten PadLeft

 
Nahoru Odpovědět 23. srpna 10:39
Avatar
TonySensu
Člen
Avatar
TonySensu:
public int CompareTo(object obj)
      {
          if (obj == null) return 1;

          Ciselnik2 otherCiselnik = obj as Ciselnik2;
          if (otherCiselnik != null)
          {
              string[] s = this.Kod.Split('.');
              string[] sOther = otherCiselnik.Kod.Split('.');

              int str1Len = s.Length;
              int str2Len = sOther.Length;

              if (str1Len < str2Len)
              {
                  //vytvořit novou instanci pole velikostne srovnanou se sOther
              }
              else if (str1Len > str2Len)
              {
                  //vytvořit novou instanci pole velikostne srovnanou se s
              }
              else
              {
                  //nemusim resit pole jsou stejne velky
              }

              //for cyklus, srovnat pocet znaku v jendotlivych polozkach pole
              string str1 = "";
              string str2 = "";
              for (int i = 0; i < str1Len; i++)
              {
                str1 += str1 + (s[i].PadLeft(2, '0'));
              }
              for (int i = 0; i < str2Len; i++)
              {
                  str2 += (sOther[0].PadLeft(2, '0'));
              }


              return str1.CompareTo(str2);
          }
          else
              throw new ArgumentException("Object is not Ciselnik2");
      }

Tak momentálně to mám takhle, ale stále to nefunguje podle představ a nevím jak to udělat..Ten for cyklus mám špatně, protože to lepím zase zpátky do jednoho stringu, mělo by to fungovat, tak že to bude porovnávat jednotlivé položky pole s druhým polem a když v nějaké části najde rozdíl tak se hned porovnávání ukončuje - tam kde byla nalezena menší hodnota je automaticky celý kod menší. Před tím - v těch Ifech je ještě potřeba srovnat délky obou polí - menší vyplnit nulovými hodnotami...Kdyby někdo věděl jak to zprovoznit tak budu velice rád :D

 
Nahoru Odpovědět 23. srpna 11:15
Avatar
Lukas C#
Redaktor
Avatar
Lukas C#:

Podle mě sis to zkomplikoval tím, že kód ukládáš jako string, a pak čekáš, že pokud to bude číselný kód, aby to řadil a porovnával jako čísla, a pokud je kód nějaké slovo (písmenka), tak aby to porovnával jako string. Metoda Sort (úplně první příspěvek) ti pole seřadila dobře, podle abecedy. Nechceš spíš překopat ten návrh? Když přemýšlím nad řešením stávajícího, tak mě napadají jenom error-prone bullshity :-D Jaký je vlastně účel tohoto porovnávání? Je to učebnicový příklad, nebo to má vyšší smysl?

 
Nahoru Odpovědět 23. srpna 11:52
Avatar
TonySensu
Člen
Avatar
Odpovídá na Lukas C#
TonySensu:

Bohužel kdyby to byla čísla tak 13.10 a 13.1 bude to stejné. A ty kódy musí být prostě string no mám to tak zadaný. Účel proč se to musí řadit takhle ani nevím mám to v zadání :D

 
Nahoru Odpovědět 23. srpna 12:03
Avatar
TonySensu
Člen
Avatar
Odpovídá na Lukas C#
TonySensu:

9.4
13.1
13.2
13.11
13.21
13.10
13.5.11
13.5.10
15.17
Ahoj

Momentálně to ty čísla seřazuje takhle, což je skoro dobře až na to, že 13.10 by mělo být před 13.11 a 13 .21 za 13.11 atd ..víš jak :D

 
Nahoru Odpovědět 23. srpna 12:08
Avatar
Lukas C#
Redaktor
Avatar
Odpovídá na TonySensu
Lukas C#:

Nemyslel jsem jako desetinná čísla, ale že každá část se přeparsuje na Integer a ty se porovnají - 13.1 a 13.10 znamená porovnat 13 vs. 13 (stejné) a pak 01 vs. 10 (01 je menší), takže pokud to chápu, seřadí to správně: 13.1 a potom 13.10. Ještě ale nechápu, v posledním příspěvku máš že např. 13.5 je až za 13.11. Pokud však porovnáváš druhou část (tzn. "5" a "11"), tak to srovnáváš na stejnou délku pomocí PadLeft, tzn. porovnává se "05" vs. "11", a "05" je přeci menší než 11. Nemělo by to být přehozené, podle zadání?

 
Nahoru Odpovědět 23. srpna 12:29
Avatar
TonySensu
Člen
Avatar
Odpovídá na Lukas C#
TonySensu:

Jo takhle mmm to by asi bylo fungovalo no, ale nevím co by to udělalo s tím "ahoj" :D to už by asi nebylo dobré :D jinak to když to má víc jak 2 části tak to jsi mě teď zmátl :D jako když se na to podívám tak bych souhlasil stebou. To budu muset ještě zjistit jak si to představujou :D

 
Nahoru Odpovědět 23. srpna 12:45
Avatar
Lukas C#
Redaktor
Avatar
Odpovídá na TonySensu
Lukas C#:

Zmátl jsem i sám sebe. Nevím vlastně, jestli se porovnává "zleva" nebo "zprava". Tj. při porovnávání kódu s jiným počtem částí např. "13.5.10" a "13.11". Porovná se "13" vs. "13" a pak "5" vs. "11", ANEBO "10" vs. "11" a pak "5" vs. "13" ? Jinak na jakou školu chodíš (jen tak ze zvědavosti).

 
Nahoru Odpovědět 23. srpna 12:56
Avatar
TonySensu
Člen
Avatar
Odpovídá na Lukas C#
TonySensu:

Určitě by to mělo být zleva :D Já už na školu nechodím letos jsem odmaturoval pak jsem si dal měsíc prázdnin a od července hurá do práce :D :D Jelikož jsem se na střední o C# neslyšel ani slovo a místo toho jsme se učili Visual Basic 6 v podstatě celý 4 roky ( -_- ), tak se všechno učím přímo při práci no :D Jinak ve volným čase jsem vždycky něco patlal v Game Makeru to mě bavilo i díky tomu jsem na tom o něco líp, než kdybych se učil jenom to co jsme dělali ve škole

 
Nahoru Odpovědět 23. srpna 13:04
Avatar
TonySensu
Člen
Avatar
TonySensu:

Přemýšlím, že brzo nahradím Game Maker za Unity, když už začínám s tím C# :D

 
Nahoru Odpovědět 23. srpna 13:05
Avatar
ostrozan
Redaktor
Avatar
Odpovídá na TonySensu
ostrozan:

co se týká toho "ahoj" - ty bys vůbec neměl dopustit, aby se ti instance třídy s nesmyslem vůbec vytvořila - nejlíp to ošetřit hned na vstupu - ovšem co je tím vstupem - jestli ten kód někdo zadává přes klávesnici, nebo je v nějakém souboru, nebo příjde po nějaké lince z venku - tos nám zatím neprozradil

ale minimálně by ten string , než ho dáš do kostruktoru měl projít aspoň něčím takovým:

foreach (char c in Kod)
{
    if(!Char.IsNumber(c)&&c!='.')
    {
        //neco proved
    }
 }

teda budeš mít jistotu, že string obsahuje pouze číslice a tečky
i když to nevyfiltruje nesmysly typu "..123" , nebo ".........."

 
Nahoru Odpovědět 23. srpna 23:20
Avatar
TonySensu
Člen
Avatar
Odpovídá na ostrozan
TonySensu:

Jak se tam dostane ten vstup to nevím, ale požadavek je, aby to i tenhle nesmysl dokázalo porovnat.

 
Nahoru Odpovědět 24. srpna 9:08
Avatar
TonySensu
Člen
Avatar
TonySensu:
class Ciselnik2 : IComparable
   {
       public string Kod { get; set; }
       public int DalsiAtribut { get; set; }
       static Random rand = new Random();
       public Ciselnik2(string kod)
       {
           this.Kod = kod;
           DalsiAtribut = rand.Next(10, 20);
       }
       public int CompareTo(object obj)
       {
           if (obj == null) return 1;

           Ciselnik2 otherCiselnik = obj as Ciselnik2;
           if (otherCiselnik != null)
           {
               string[] s = this.Kod.Split('.');
               string[] sOther = otherCiselnik.Kod.Split('.');

               int str1Len = s.Length;
               int str2Len = sOther.Length;

               var sList = s.ToList();
               var s2List = sOther.ToList();

               if (str1Len < str2Len)
               {

                   //string[] newS = new string[];

                   for (int i = 0; i < (str2Len - str1Len); i++)
                   {
                       sList.Add("0");
                   }

               }
               else if (str1Len > str2Len)
               {


                   for (int i = 0; i < (str1Len - str2Len); i++)
                   {
                       s2List.Add("0");
                   }
               }
               else
               {
                   //nemusim resit pole jsou stejne velky
               }



               //for cyklus, srovnat pocet znaku v jendotlivych polozkach pole
               for (int i = 0; i < sList.Count; i++)
               {
                   if (sList[i].Length > s2List[i].Length) {
                       sList[i].PadLeft(sList[i].Length, '0');
                       s2List[i].PadLeft(sList[i].Length, '0');
                   }
                   else if (sList[i].Length < s2List[i].Length)
                   {
                       sList[i].PadLeft(s2List[i].Length, '0');
                       s2List[i].PadLeft(s2List[i].Length, '0');
                   }
               }

               int porovnej = 666;
               for (int i = 0; i < sList.Count; i++)
               {
                   porovnej = sList[i].CompareTo(s2List[i]);
                   if (porovnej < 0 || porovnej > 0)
                   {
                       return porovnej;
                       //break - zjistil ze polozka pole je mensi nebo vetsi
                   }
               }
               //jinak dojde az nakonec a vrati vysledek
               return porovnej;
           }
           else
               throw new ArgumentException("Object is not Ciselnik2");
       }

Teď jsem to zkusil takhle upravit, ale nefunguje to :D :(

Výsledek: 13.1
13.10
13.11
13.2
13.21
13.5.10
13.5.11
15.17
9.4
Ahoj

Editováno 24. srpna 9:13
 
Nahoru Odpovědět 24. srpna 9:11
Avatar
DZetko
Člen
Avatar
DZetko:

Je součástí zadání implementovat IComparable a List<>.Sort()?

 
Nahoru Odpovědět 24. srpna 15:20
Avatar
Odpovídá na TonySensu
Ondřej Štorc:

Nezkoušel jsem to ale měl by to takhle fungovat. Kdyby jsi měl dotazy k kódu, případně námitky že se dá něco udělat lépe (určitě dá) tak se ozvi:

public int CompareTo(object obj)
{
    if (obj == null) return 1;
    if (!(obj is Ciselnik2)) throw new ArgumentException();

    Ciselnik2 ciselnik = obj as Ciselnik2;

    Regex regex = new Regex("[0-9]+[.][0-9]+");
    // Ale měl by jsi to už ověřovat při vzniku...
    if (!regex.IsMatch(this.Kod) && regex.IsMatch(ciselnik.Kod)) return -1;
    if (!regex.IsMatch(ciselnik.Kod) && regex.IsMatch(this.Kod)) return 1;

    if (this.Kod == ciselnik.Kod) return 0;

    var thisKod = Kod.Split('.');
    var objKod = ciselnik.Kod.Split('.');

    if (thisKod[0] == objKod[0])
    {
        return int.Parse(thisKod[1]).CompareTo(int.Parse(objKod[1]));
    }
    return int.Parse(thisKod[0]).CompareTo(int.Parse(objKod[0]));

}
Akceptované řešení
+20 Zkušeností
+1 bodů
Řešení problému
Nahoru Odpovědět 24. srpna 15:49
Život je příliš krátký na to, abychom bezpečně odebírali USB z počítače..
Avatar
TonySensu
Člen
Avatar
Odpovídá na Ondřej Štorc
TonySensu:

Seš borec - funguje to :D Moc díky :)

 
Nahoru Odpovědět 24. srpna 15:54
Avatar
TonySensu
Člen
Avatar
Odpovídá na Ondřej Štorc
TonySensu:

Jen teda vidím, že to funguje jen na kody, který maj dvě časti - 13.10, 13.1...potřebuju, aby to šlo na jakkoliv dlouhej kod např. 13.5.10. Zkusím to sám upravit, kdyby se mi to nepodařilo tak se ozvu :D

 
Nahoru Odpovědět 25. srpna 8:13
Avatar
TonySensu
Člen
Avatar
Odpovídá na Ondřej Štorc
TonySensu:
public int CompareTo(object obj)
      {
          if (obj == null) return 1;
          if (!(obj is Ciselnik2)) throw new ArgumentException();

          Ciselnik2 ciselnik = obj as Ciselnik2;

          Regex regex = new Regex("[0-9]+[.][0-9]+");

          if (!regex.IsMatch(this.Kod) && regex.IsMatch(ciselnik.Kod)) return -1;
          if (!regex.IsMatch(ciselnik.Kod) && regex.IsMatch(this.Kod)) return 1;

          if (this.Kod == ciselnik.Kod) return 0;

          var thisKod = Kod.Split('.');
          var objKod = ciselnik.Kod.Split('.');

          int str1Len = thisKod.Length;
          int str2Len = objKod.Length;

          var sList = thisKod.ToList();
          var s2List = objKod.ToList();

          if (str1Len < str2Len)
          {



              for (int i = 0; i < (str2Len - str1Len); i++)
              {
                  sList.Add("0");
              }

          }
          else if (str1Len > str2Len)
          {


              for (int i = 0; i < (str1Len - str2Len); i++)
              {
                  s2List.Add("0");
              }
          }

          int porovnej = 666;
          for (int i = 0; i < s2List.Count; i++)
          {
              porovnej = int.Parse(sList[i]).CompareTo(int.Parse(s2List[i]));
              if (porovnej < 0 || porovnej > 0)
              {
                  return porovnej;

              }
          }

          return porovnej;

      }

Už funguje jak má :)

 
Nahoru Odpovědět  +1 25. srpna 8:32
Avatar
TonySensu
Člen
Avatar
TonySensu:

Tak jsme nakonec zjistili, že moje verze byla správně, ale jen chybělo uložení po provedeni PadLeft jelikož to pouze vrací ale nenastavuje. Takže jen taková miniaturní úprava a tenhle kod funguje suprově. Jsem rád, že to tu konečně můžu uzavřít :D

public int CompareTo(object obj)
      {
          if (obj == null) throw new ArgumentNullException(nameof(obj));

          Specifikace otherSpecifikace = obj as Specifikace;

          if (otherSpecifikace == null)
              throw new ArgumentException("Object is not Specifikace");


          string[] thisArrayKod = this.Kod.Split('.');
          string[] otherArrayKod = otherSpecifikace.Kod.Split('.');

          int thisKodArrayLen = thisArrayKod.Length;
          int otherKodArrayLen = otherArrayKod.Length;

          var thisListKod = thisArrayKod.ToList();
          var otherListKod = otherArrayKod.ToList();

          if (thisKodArrayLen < otherKodArrayLen)
          {

              for (int i = 0; i < (otherKodArrayLen - thisKodArrayLen); i++)
              {
                  thisListKod.Add("0");
              }

          }
          else if (thisKodArrayLen > otherKodArrayLen)
          {

              for (int i = 0; i < (thisKodArrayLen - otherKodArrayLen); i++)
              {
                  otherListKod.Add("0");
              }
          }

          for (int i = 0; i < thisListKod.Count; i++)
          {

              if (thisListKod[i].Length > otherListKod[i].Length)
              {

                  otherListKod[i] = otherListKod[i].PadLeft(thisListKod[i].Length, '0');
              }
              else if (thisListKod[i].Length < otherListKod[i].Length)
              {
                  thisListKod[i] = thisListKod[i].PadLeft(otherListKod[i].Length, '0');

              }
          }

          int compareResult = 0;
          for (int i = 0; i < thisListKod.Count; i++)
          {
              compareResult = thisListKod[i].CompareTo(otherListKod[i]);
              if (compareResult != 0)
              {
                  return compareResult;

              }
          }

          return 0;
      }

btw názvy už jsem změnil, aby mi to sedělo do real projektu.

Editováno 25. srpna 16:00
 
Nahoru Odpovědět  +1 25. srpna 15:59
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 32 zpráv z 32.