C# Sortieren von Werten

Neue Frage »

Auf diesen Beitrag antworten »
InformaTiger C# Sortieren von Werten

Hallo,
ich programmiere gerade einen Selfmonitor der mir anhand von Werten die in einer Datenbank gespeichert sind einen bzw. mehrere Graphen zeichnen soll. Die Schwierigkeit besteht für mich darin die Werte so einzusetzen damit ich mir möglichst Ressourcen sparend die Graphen zeichnen kann. Mein bisheriger Ansatz ist so:

code:
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
            Database_Request request = new Database_Request();
            string[] arr = request.ReadFromTable("SELECT tbl_values.Datum, tbl_values.Wert, tbl_categories.Name, tbl_categories.Farbe FROM tbl_values INNER JOIN tbl_categories ON tbl_categories.ID = tbl_values.FK_Kategorie;");
            List<DateTime> datum = new List<DateTime>();
            List<int> bewertung = new List<int>();
            List<string> kategorie = new List<string>();
            List<string> farbe = new List<string>();

            for (int i = 0; i < arr.Length; i++)
            {
                switch (i % 4)
                {
                    case 0:
                        {
                            // Datum
                            break;
                        }
                    case 1:
                        {
                            // Bewertung
                            break;
                        }
                    case 2:
                        {
                            // Kategorie
                            break;
                        }
                    case 3:
                        {
                            // Farbe
                            break;
                        }
                }
            }


Ich bin der Meinung das müsste einfacher gehen um sich die vielen Listen zu sparen. Leider weiß ich nicht wie genau aber ich könnte mir vorstellen es hätte was mit mehrdimensonalen Arrays zu tun.

verwirrt

Mfg
InformaTiger
 
Auf diesen Beitrag antworten »
eulerscheZahl

erstelle doch eine Klasse, in der du datum, bewertung, kategorie und farbe speicherst:
code:
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
class Foo{
    DateTime datum;
    int bewertung;
    string kategorie, farbe;

    public Foo(DateTime datum, int bewertung, string kategorie, string farbe){
        this.datum = datum;
        this.bewertung = bewertung;
        this.kategorie = kategorie;
        this.farbe = farbe;
    }
}

List<Foo> liste = new List<Foo>();
for(int i = 0; i < arr.Length / 4; i++)
{
    liste.Add(new Foo(new DateTime(arr[4*i]), int.Parse(arr[4*i+1]), arr[4*i+2], arr[4*i+3]));
}
Auf diesen Beitrag antworten »
InformaTiger

Danke!

Auf diese Idee bin ich logischerweise wieder mal nicht gekommen.

Inzwischen bin ich auf ein weiteres Problem gestoßen. Wenn ich den Graph mittels DrawLine zeichne:

code:
1:
2:
3:
4:
            for (int i = 0; i < datum.Count - 1; i++)
            {
                g.DrawLine(new Pen(new SolidBrush(ColorTranslator.FromHtml('#' + farbe[i].ToLower())), 2.0f), (datum[i].Day + 0.5f) * del, bewertung[i] * del, (datum[i + 1].Day + 0.5f) * del, bewertung[i + 1] * del);
            }


Sieht das Ergebnis wie im Anhang aus, also muss ich es zusätzlich noch irgendwie schaffen auszulesen wann eine neue Kategorie von der Schleife bearbeitet wird.

Mfg
InformaTiger
Auf diesen Beitrag antworten »
eulerscheZahl

so vielleicht:
code:
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
string letzteKategorie = kategorie[0];
int x = 0;
for(int i = 1; i < datum.Count; i++)
{
    if (letzteKategorie != kategorie[i])
    {
        letzteKategorie = kategorie[i];
        x = 0; //wieder links anfangen
    }
    else
    {
        drawline(...) //in Abhängigkeit von x, bewertung[i-1], bewertung[i]
        x++;
    }
}
 
Auf diesen Beitrag antworten »
InformaTiger

Das Problem hierbei besteht eben darin dass die Werte nicht sortiert nach Kategorie gespeichert sind, weil der Benutzer ja in beliebiger Reihenfolge die Werte in die Tabellen schreiben kann. Kann man die Liste aus Objekten die wir vorhin angelegt haben nach Kategorie sortieren?

Mfg
InformaTiger
Auf diesen Beitrag antworten »
eulerscheZahl

Bin gerade mit Linux unterwegs, daher ohne Syntaxprüfung:
code:
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
public class Foo : IComparable
{
 //...
    public int CompareTo(object obj)
    {
        Foo comp = (Foo)obj;
        return this.kategorie.CompareTo(comp.kategorie);
    }
}

dann einfach über liste.Sort();


hier noch ein ausführlicheres Beispiel: msdn.microsoft.com
Auf diesen Beitrag antworten »
InformaTiger

Ich habe soeben diese Methode getestet, mein Ergebnis war alles andere als erfreulich: ein totales durcheinander aus Strichen. Allerdings habe ich das Problem durch einen ORDER BY beim SQL-Command gelöst. Den Code habe ich jetzt folgendermaßen erweitert:

code:
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
            Database_Request request = new Database_Request();
            string[] arr = request.ReadFromTable("SELECT tbl_values.Datum, tbl_values.Wert, tbl_categories.Name, tbl_categories.Farbe FROM tbl_values INNER JOIN tbl_categories ON tbl_categories.ID = tbl_values.FK_Kategorie WHERE MONTH(Datum) = " + DateTime.Now.Month + " ORDER BY tbl_categories.Name;");
            List<DBEntry> entries = new List<DBEntry>();
            List<string> kategorien = new List<string>();

            for (int i = 0; i < arr.Length / 4; i++)
            {
                entries.Add(new DBEntry(DateTime.Parse(arr[4 * i]), int.Parse(arr[4 * i + 1]), arr[4 * i + 2], arr[4 * i + 3]));
                kategorien.Add(arr[4 * i + 2]);
            }

            kategorien = kategorien.Distinct().ToList();

            g.Restore(gs);

            for (int i = 0; i < kategorien.Count; i++)
            {
                int count = entries.Count(c => c.kategorie == kategorien[i]);

                for (int j = 0; j < count - 1; j++)
                {
                    g.DrawLine(new Pen(new SolidBrush(ColorTranslator.FromHtml('#' + entries[j].farbe.ToLower())), 2.0f), (entries[j].datum.Day + 0.5f) * del, entries[j].bewertung * del, (entries[j + 1].datum.Day + 0.5f) * del, entries[j + 1].bewertung * del);
                }
            }


Funktionieren laut Haltepunkte müsste es, aber grafisch sehe ich jetzt nur mehr den 1 Graphen.

Edit: habe meinen Fehler gefunden, ich habe den ersten Graphen 2 mal gezeichnet. ich muss jetzt da ich die Anzahl jeder Kategorie habe nur herausfinden wie ich zurücksetzen kann sodass nach jeder Kategorie abgebrochen und bei dem neuen Datum wieder begonnen wird.

Mfg
InformaTiger
Auf diesen Beitrag antworten »
eulerscheZahl

Hatte dein edit nicht gesehen, falls es noch aktuell ist:
Mein Code macht das, was in Anhang zu sehen ist, ich dachte, das wolltest du?

code:
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
Random r = new Random(0);
string[] arr = new string[1000];
List<Foo> liste = new List<Foo>();
for (int i = 0; i < arr.Length / 4; i++)
{
    liste.Add(new Foo(DateTime.Now, r.Next(100), ((char)('a' + r.Next(5))).ToString(), "rot"));
}
liste.Sort();
Bitmap bmp = new Bitmap(500, 500);
Graphics g = Graphics.FromImage(bmp);
g.Clear(Color.White);
string letzteKategorie = liste[0].kategorie;
Color cl = new Color();
int x = 0;
for (int i = 1; i < liste.Count; i++)
{
    if (letzteKategorie != liste[i].kategorie)
    {
        letzteKategorie = liste[i].kategorie;
        x = 0; //wieder links anfangen
        cl = Color.FromArgb(r.Next(1 << 8), r.Next(1 << 8), r.Next(1 << 8));
    }
    else
    {
        g.DrawLine(new Pen(new SolidBrush(cl), 2.0f), 5*x, 5*liste[i - 1].bewertung, 5*(x + 1), 5*liste[i].bewertung); //in Abhängigkeit von x, bewertung[i-1], bewertung[i]
        x++;
    }
}
pictureBox1.Image = bmp;
Auf diesen Beitrag antworten »
InformaTiger

So... dank der Hilfe meines Bruders ist es mir jetzt endlich gelungen. Der Code dazu sieht so aus:

code:
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
            Database_Request request = new Database_Request();
            string[] arr = request.ReadFromTable("SELECT tbl_values.Datum, tbl_values.Wert, tbl_categories.Name, tbl_categories.Farbe FROM tbl_values INNER JOIN tbl_categories ON tbl_categories.ID = tbl_values.FK_Kategorie WHERE MONTH(Datum) = " + DateTime.Now.Month + " ORDER BY tbl_categories.Name, tbl_values.Datum;");
            List<DBEntry> entries = new List<DBEntry>();
            List<string> kategorien = new List<string>();

            for (int i = 0; i < arr.Length / 4; i++)
            {
                entries.Add(new DBEntry(DateTime.Parse(arr[4 * i]), int.Parse(arr[4 * i + 1]), arr[4 * i + 2], arr[4 * i + 3]));
                kategorien.Add(arr[4 * i + 2]);
            }

            kategorien = kategorien.Distinct().ToList();

            g.Restore(gs);

            int count = 0;

            for (int i = 0; i < kategorien.Count; i++)
            {
                int old;
                old = count;
                count += entries.Count(c => c.kategorie == kategorien[i]);

                if (count - old == 1)
                {
                    g.DrawLine(new Pen(new SolidBrush(ColorTranslator.FromHtml('#' + entries[old].farbe.ToLower())), 2.0f), (entries[old].datum.Day + 0.5f) * del, entries[old].bewertung * del, (entries[old].datum.Day + 1) * del, entries[old].bewertung * del);
                }
                else
                {
                    for (int j = old; j < count - 1; j++)
                    {
                        g.DrawLine(new Pen(new SolidBrush(ColorTranslator.FromHtml('#' + entries[j].farbe.ToLower())), 2.0f), (entries[j].datum.Day + 0.5f) * del, entries[j].bewertung * del, (entries[j + 1].datum.Day + 0.5f) * del, entries[j + 1].bewertung * del);
                    }
                             
                }
                
            }


Die Variable old speichert jeweils den letzten Stand und sorgt dafür dass Einträge die alleine stehen und noch keinen Referenzpunkt haben auch angezeigt werden. Wichtig war auch bei der SQL Anweisung eine sekundäre Sortierung hinzuzufügen: ORDER BY tbl_categories.Name, tbl_values.Datum;

PS: wenn du noch was verbessern wüsstest, bitte einfach nur machen Augenzwinkern

Danke für deine Hilfe!

Mfg
InformaTiger
Auf diesen Beitrag antworten »
eulerscheZahl

Wenn du schon so fragst, das gefällt mir nicht:
code:
1:
2:
kategorien.Add(arr[4 * i + 2]);
kategorien = kategorien.Distinct().ToList();

Ich weiß nicht, wie viele Einträge du bei deiner SQL-Abfrage erhältst, aber die musst du alle zwischenspeichern, um dann den Großteil wieder zu löschen.

Ich würde das hier bevorzugen:
code:
1:
2:
if (kategorien.Contains(arr[4*i + 2])
    kategorien.Add(arr[4*i + 2]);


Je nach Anzahl der Kategorien könnte es Sinn machen, die Liste sortiert zu halten, das beschleunigt das Suchen.
Das ginge so:
code:
1:
2:
3:
4:
5:
6:
        int index = dinosaurs.BinarySearch("Coelophysis");
        if (index < 0)
        {
            dinosaurs.Insert(~index, "Coelophysis");
        }

(von Microsoft)
Auf diesen Beitrag antworten »
InformaTiger

Also wenn ich das richtig verstanden habe, was du zu bemängeln hast dann ist das Prinzip dasselbe wie das hier:

code:
1:
2:
3:
4:
5:
int index = kategorien.IndexOf(arr[4 * i + 2]);
if (index < 0)
{
    kategorien.Add(arr[4 * i + 2]);
}


Nur, dass du mit einer anderen Methode den index bekommst als ich in dem Beispiel. Sortiert ist meine Liste ja ohnehin schon, da ich sie mit dem ORDER BY Kommando sortiert aus der Datenbank erhalte. Wobei ich sagen muss dass der zweite Code von dir noch effizienter ist.

code:
1:
2:
3:
4:
if (!kategorien.Contains(arr[4 * i + 2]))
{
    kategorien.Add(arr[4 * i + 2]);
}


Mfg
InformaTiger
Auf diesen Beitrag antworten »
Karlito

Hallo,

wenn es Kategorien nicht doppelt geben kann, warum verwendet ihr dann nicht ein Set? Sets entsprechen mathematischen Mengen und sind so gebaut, dass sie effizient ermöglichen Elemente nur einmalig aufzunehmen (In Mengen kann es ja auch keine Doppelten geben). Bei einem HashSet hat man idealisiert konstante Laufzeit, d.h. jeder Einfügevorgang (mit Prüfung auf doppelte) und jeder Suchvorgang (contains()) dauert idealisiert eine konstante Zeit, unabhängig von der Anzahl der Elemente (in der Realität klappt das nicht ganz). Problem ist, dass ein HashSet nicht sortiert ist.
Für sortierte Mengen gibt es noch das SortedSet. Ich denke das hat dann logarithmische Laufzeit (auch sehr gut). Jedenfalls spart man sich die Prüfung auf doppelte und das selektive Einfügen.

Gruß,

Karlito
Auf diesen Beitrag antworten »
eulerscheZahl

Guter Einwand, Karlito.

Noch ein Vorschlag, wie es auch mit einer einfachen List effizient geht: da bereits nach kategorie sortiert ist, reicht es, den letzten Eintrag zu prüfen:
code:
1:
2:
if (kategorien[kategorien.Count - 1] != arr[4*i + 2])
    kategorien.Add(arr[4 * i + 2]);
Auf diesen Beitrag antworten »
Karlito

Hallo euler, hallo InformaTiger,

auch wenn man es vielleicht so biegen kann, dass eine Liste effizient wird, so würde ich mich darauf verlassen, das die Implementierung des Sets effiizienter ist. Schließlich ist sie für genau diesen Zweck gebaut. Ansonsten würde ich eine Liste, welche optimiert ist selbst implementieren.
Mich würde an der Stelle mal ein Vergleich Interessieren: 1 Mio eindeutige Einfügevorgänge für eine Liste und das Selbe für ein Set. Ich würde darauf tippen, dass das Set gewinnt. Spätestens beim Speicherverbrauch.

Sorry, wenn die Diskussion hier zu weit geht.

Gruß,

Karlito
Auf diesen Beitrag antworten »
InformaTiger

Hallo,
entschuldigt bitte, dass ich mich nicht mehr gemeldet habe. Ich hatte in letzter Zeit nur etwas Stress unglücklich . Dann sollte ich also ein Set verwenden und die Geschichte wäre von der Preformance her, gegessen?

Mfg
InformaTiger
Auf diesen Beitrag antworten »
Karlito

Ich denke ja, es sollte schneller werden.

Gruß,

Karlito
Auf diesen Beitrag antworten »
InformaTiger

Wenn ich jetzt aber ein Set verwende kann ich mir dann die if-Abfrage sparen, oder? Er nimmt Werte ja nur einmalig auf oder hab ich da etwas falsch verstanden?

Mfg
InformaTiger
Auf diesen Beitrag antworten »
Karlito

genau
 
Neue Frage »
Antworten »


Verwandte Themen

Die Beliebtesten »
Die Größten »
Die Neuesten »