Diskuze: C# IComparable, Algoritmus porovnávání
V předchozím kvízu, Test znalostí C# .NET online, jsme si ověřili nabyté zkušenosti z kurzu.
Člen
Zobrazeno 32 zpráv z 32.
//= Settings::TRACKING_CODE_B ?> //= Settings::TRACKING_CODE ?>
V předchozím kvízu, Test znalostí C# .NET online, jsme si ověřili nabyté zkušenosti z kurzu.
Jestli máš implementované rozhraní IComparable, tak sem postni tvojí verzi metody Compare (tzn. cos zatím zkoušel). Podíváme se na to.
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.
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 ) a nějaky pokus, který jsem zakomentoval - netuším, jak to tam správně zakomponovat.
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ů
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.
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
Ve výsledku žádný rozdíl. Jak to myslíš v tom konstruktoru?
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.
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..
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
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 Jaký je vlastně účel tohoto porovnávání? Je to učebnicový příklad, nebo to má vyšší smysl?
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í
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
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í?
Jo takhle mmm to by asi bylo fungovalo no, ale nevím co by to udělalo s tím "ahoj" to už by asi nebylo dobré jinak to když to má víc jak 2 části tak to jsi mě teď zmátl jako když se na to podívám tak bych souhlasil stebou. To budu muset ještě zjistit jak si to představujou
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).
Určitě by to mělo být zleva 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 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 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
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
".........."
Jak se tam dostane ten vstup to nevím, ale požadavek je, aby to i tenhle nesmysl dokázalo porovnat.
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
Výsledek: 13.1
13.10
13.11
13.2
13.21
13.5.10
13.5.11
15.17
9.4
Ahoj
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]));
}
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
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á
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
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.
Zobrazeno 32 zpráv z 32.