Schlüssel-Wert Wert-Schlüssel

  • VB.NET

Es gibt 17 Antworten in diesem Thema. Der letzte Beitrag () ist von ~blaze~.

    Schlüssel-Wert Wert-Schlüssel

    Man stelle sich eine Liste von Beziehungen vor

    Otto-Vera
    Fritz-Waltraud
    Oskar-Anna
    Werner-Frieda


    Wie speichert man solche Beziehungen am besten, wenn man später für Oskar die Anna
    und für Frieda den Werner zurück bekommen will?

    Man könnte zwei Arrays anlegen und den gefundenen Index im einen Array als Index für
    das andere Array verwenden.

    Man könnte eine Tabelle erzeugen, und dann per SQL suchen.

    Man könnte irgendwelche Collections oder Listen erstellen...

    Gibt es eine Struktur, die für solche Probleme besonders gut geeignet ist.
    So etwas kommt schließlich immer wieder vor.
    Hi
    ich denke, es kommt darauf an, worauf du Wert legst. Es gibt viele Möglichkeiten das zu lösen - z.B. Dictionary, Hashtable oder quasi hybrid aus den beiden. Sind die Daten flexibel? Kann z.B. die Instanz von Oskar ihren Schlüssel ändern oder sind diese fest? Häufig genügt eine einfache Struktur, bei der du einfach eine Klasse mit den Schlüssel-Wert-Paaren hast. Anschließend implementierst du bspw. eine ICollection oder ein IDictionary entsprechend, aber das kommt auf den Verwendungszweck, die Datenmengen usw. an. Manchmal genügen auch Indizes, die z.B. innerhalb der Instanz von Frieda auf den Index von Werner gesetzt werden oder Klassen, die auf Werner zeigen und eine indizierte Verwaltung oder eine Referenz auf das Schlüssel-Wert-Paar bereithalten usw.
    Wie sind deine Daten gestaltet, welche Datenmengen erwartest du, können sich die Daten verändern, kommen sortierte Listen, Hashtabellen usw. in Frage?
    Die Indizierung ist relativ einfach, aber häufig unperformant, da du sämtliche Elemente durchgehen müsstest. Eine sortierte Liste ist nicht möglich, da sich dann die Indizes ändern, wenn neue Elemente hinzukommen. Bei Hashtabellen hast du das Problem nicht, aber wenn sich der Schlüssel ändert, ist es wahrscheinlich, dass sich auch der Hash ändert. Da du häufig die Daten nicht explizit an eine Liste binden möchtest, kannst du evtl. auch die Indizes nicht den Instanzen mitgeben. Wenn du wiederum ein Dictionary verwendest, um die Instanzen mit zusätzlichen, indizierenden Instanzen zu verknüpfen, ist das Dictionary wieder Flaschenhals. Daher lässt sich halt keine pauschale Aussage treffen.
    SQL könnte man auf jeden Fall verwenden, aber programmintern ist das häufig nicht notwendig.

    Gruß
    ~blaze~
    eine sehr einfache Möglichkeit ist eine List(Of DeineDatenklasse)
    List(Of T) hat die FindIndex-Funktion, und da kann man auch ein Predicate übergeben

    VB.NET-Quellcode

    1. (dim Pairs as new List(Of Pair))
    2. 'Index holen:
    3. dim iFritzWaltraud = pairs.FindIndex(function(p)p.Male = "Fritz")
    4. iFritzWaltraud = pairs.FindIndex(function(p)p.FeMale = "Waltraud")
    5. 'oder direkt das Item:
    6. dim fritzWaltraud As Pair = pairs.Find(function(p)p.Male = "Fritz")
    7. fritzWaltraud = pairs.Find(function(p)p.FeMale = "Waltraud")
    Ausserdem kann man mit Linq auf List(Of T) losgehen, damit hat man eiglich alle Möglichkeiten, die zB auch das Sql-Select-Statement bereitstellt.

    Nur halt typisiert und ohne DB-Krampf.
    @ErfinderDesRades

    So ähnlich hatte ich es schon mal mit
    XML-Daten gemacht.
    Ich hatte dann aber beim Debuggen
    ziemlich Probleme, bis ich mir den Vorgang
    wieder bildhaft vorstellen konnte. ;(

    Bei Collections finde ich auch schade, dass man
    die Keys nicht beliebig wählen kann.
    Beispielsweise Klein und Großbuchstaben
    oder Sonderzeichen.

    Das was ich im Moment grade mache ist eigentlich
    ziemlich simpel. Mir ist nur wieder in den Sinn gekommen,
    dass ich schon oft mit genau diesen Problemen zu
    kämpfen hatte. Nämlich vom Schlüssel den Wert, und
    vom Wert auf den Schlüssel zu kommen.

    Ich bin gerade an einer Umrechnung für physikalische
    Größen. Die verschiedenen Potenzen haben ja
    verschiedene Kurzzeichen (p,n,µ,m,,k,M,G,T)
    Ich will also nur von dem benutzten Kurzzeichen
    auf die Potenz kommen bzw. von der Potenz auf
    das Kurzzeichen. Das kann ich natürlich auch mit
    Select Case erreichen...

    Lightsource schrieb:

    Bei Collections finde ich auch schade, dass man
    die Keys nicht beliebig wählen kann.

    k.A., was du meinst. Imo kann man bei Collection den Key durchaus wählen, und wenn man die aufgezeigten Finde-Methoden nimmt, dann kann man sogar noch viel differenzierter differenzieren, als nur Groß/Kleinschreibung oder Zeugs.
    Da kann man ja theor. sonstwelche Ähnlichkeitsbedingungen ebensogut angeben.
    achdugrüneNeune!

    Da hast du ja die olle vb6-Collection am Wickel!
    Die ist unbrauchbar für ein .Net-Proggi.

    Wenn ich Collection sage, meine ich moderne Auflistung, also eine Klasse, die mindestens ICollection implementiert, also T-Array, List(Of T), BindingList(Of T), DataTableBase(Of T), ObservableCollection(Of T), Dictionary(Of Tkey, TValue), Stack(Of T), .....
    also wieder zurück auf post#3, und eine Datenklasse proggen. Für Waltraud und Fritz habe ich die Klasse ja "Pair" genannt, aber für deine Maßeinheiten sollte sie einen anneren Namen führen, und auch nicht nur Strings enthalten, sondern auch einen Double oder Decimal oder sowas.
    Besser wäre es doch eigentlich, einen Wert zu normalisieren und durch eine Einheit nur darzustellen µm z.B. ist halt dann 10^-6 und die Einheit, in der die Daten festgehalten werden, ist m. Bei der Darstellung wird dann automatisch die Einheit genommen, die dem Wert am ehesten entspricht, wie z.B. eben µm. Das Problem bei einem Dictionary ist, dass es ineffizient ist und wenn man schon die Möglichkeit hat, sollte man auf eine andere Struktur oder Architektur ausweichen.

    Gruß
    ~blaze~
    von einem Dictionary rede ich garnet.

    Aber ich glaub ich versteh, was du meinst: Also einfach nur die Kennbuchstaben in ein Array, das Array als DataSource einer Listbox, und dann anhand des .SelectedIndex der Listbox iwie umrechnen zum gewünschten Maßeinheiten-Faktor. Und in die annere Richtung kann man ja auch rechnen.

    Ist halt ein annerer Ansatz, wobei ich LightSource so verstanden hab, dass er eine generelle Lösung sucht, wie man in Tabellen mit mindestens 2 Spalten ("Schlüssel - Wert") Datensätze auffindet, sowohl von Schlüssel zu Wert als auch von Wert zu Schlüssel. Also im Grunde eine "Schlüssel - Schlüssel"-Tabelle.
    Noch genereller sucht er, wie man in Schlüssel-Tabellen (es sind ja noch mehr Spalten möglich) nach verschiedenen Spalten Datensätze auffindet.

    Und da bietet sich halt Linq an, bzw olle List(Of TDatenklasse) macht den Job auch schon.

    ~blaze~ schrieb:

    Besser wäre es doch eigentlich, einen Wert zu normalisieren und durch eine Einheit nur darzustellen µm z.B. ist halt dann 10^-6 und die Einheit, in der die Daten festgehalten werden, ist m. Bei der Darstellung wird dann automatisch die Einheit genommen, die dem Wert am ehesten entspricht, wie z.B. eben µm. Das Problem bei einem Dictionary ist, dass es ineffizient ist und wenn man schon die Möglichkeit hat, sollte man auf eine andere Struktur oder Architektur ausweichen.



    Ja, an so etwas bin ich ja dran, wie man an meinem anderen ENG Thread sehen kann.

    Es geht um eine Art Verdünnungsberechnungsprogramm. Ich hatte so etwas schon vor etlichen Jahren mit VB6
    erstellt. Hat ganz gut funktioniert. Wollte es nun neu in VB.Net schreiben.

    Im Prinzip geht es um Umrechnungen von %, ppm, mg/liter, Dichte, Mol etc.
    Man wiegt eine Substanz in einen Maßkolben, entnimmt ein bestimmtes Volumen oder Masse
    und verdünnt das im nächsten Kolben. somit hat man eine neue Konzentration.
    Die Anzahl der Verdünnungsstufen wollte ich wieder dynamisch machen.
    Auch soll eine Optimierungsberechnung eingebaut sein, die in der Lage wäre
    die Verdünnung mit dem kleinsten Fehler zu berechnen. Das Ganze soll aber auch
    rückwärts rechnen können. Also ich will eine bestimmte Endkonzentration.
    Wie sind die optimalen Verdünnungsschritte, bei denen ich dann auch so wenig wie
    möglich an Substanz verbrauche. (Darum auch die Umkehrung der Schlüssel)

    Ich hatte ComboBoxen mit vorwählbaren Kolben und Pipettengrößen verwendet,
    die man aber auch überschreiben konnte.
    Die Einheiten und Größen waren frei wählbar und haben automatisch zwischen
    ppm,mg/kg und beispielsweise ppb,µg/kg mit Dichtefunktion umgerechnet usw.
    Stimmt schon, für allgemeine Fälle, wäre das eine schöne Lösung. Problematisch ist halt nur, dass ein "dummes" List(Of X) - d.h. unsortiert, usw. - erfordert, dass man jedes Element durchgeht und Zutreffen auf eine Bedingung überüprüft. Bei einem List(Of T) geht man halt genau davon aus, weil von der Klasse selbst keine Einschränkung getroffen wird. Linq basiert auf Enumeratoren, denk' ich mal? Also es wird einfach ein IEnumerator definiert, der auf Basis einer gegebenen IEnumerable sämtliche Werte durchgeht und für jeden die Aussagen und Funktionen überprüft und entsprechende Statements ausführt, sofern der Enumerierende MoveNext aufruft? Wenn dem so ist, dürfte das ebenfalls auf einen Flaschenhals treffen, wenn es performantere Arten der Selektion gäbe. Eine Abstraktion hat das Problem, dass der auf der Abstraktion arbeitende nicht weiß (bzw. nicht wissen sollte - Extensions sehen das anders), was für eine Art der Implementierung da vergraben ist. Z.B. fallen binäre Suche auf eine IEnumerable weg, da es sich nicht zwangsweise um eine indizierende Implementierung handelt. Ein Check auf IList ist nicht angebracht, da Indizierbarkeit nicht IList impliziert. Bei einer Selektion wäre eben genau diese Forderung einer binären Suche häufig möglich, aber das fällt weg, wenn nur auf IEnumerable gearbeitet wird, da ein IEnumerable nur
    - das nächste Element wählen
    - das aktuelle Element zurückgeben
    kann (und paar, jetzt, Nebensachächlichkeiten). Auf IList zu prüfen nimmt der Abstraktion die Abstraktion, dann hätte man es auch gleich für eine ausgewählte Menge diskreter Unterklassen implementieren können.
    Die performanteste Umgehung, auf die ich bisher gekommen bin, ist, einfach einen Hash des Strings zu erzeugen und den als Schlüssel als Index zu verwenden (z.B. ein Integer per XOR erzeugen und anschließend 8 Bit zur Indizierung herausnehmen). Da man somit bereits eine stark eingeschränkte Untermenge selektieren kann, ist der eigentliche Suchvorgang noch mal viel höher, wenn die Tiefe der Hashtabelle sinnvoll gehalten wird. Das schränkt bei einem eigentlich großen Dictionary die Zahl der zu vergleichenden Werte stark ein und die Hashberechnung ist bei XOR z.B. sehr viel performanter, als ein Vergleich mit sehr vielen Werten. Bei einer sinnvollen Wahl der Größe der Hashtabelle verschwendet man übrigens auch nicht so viel Speicher. Danach kann man die Untermengen auch noch sortieren, wenn oft auf Werte zugegriffen wird.
    Bei einer einfachen Selektion von Einheiten wäre das aber ein kleiner Overkill :P. Jetzt bin ich wohl etwas abgeschweift. Was ich eigentlich sagen wollte: Linq, Find, FindIndex usw. machen einem das Leben zwar einfacher, aber die Einfachheit führt halt häufig dazu, dass man sich ins Bein schießt. In den meisten Fällen kommt Optimierung erst zum Schluss, falls überhaupt, aber es ist schwieriger, Linq zur Implementierung eines abstrakten Typen umzuwandeln, als wenn man z.B. direkt auf einer ICollection arbeitet. Abstraktion kann man schön dazu verwenden, Dinge später auszutauschen, während ein Arbeiten auf diskreten Typen eben sehr schwer gutzumachen sein kann. Interfaces sind eine möglichst geringe Menge an unterstützten Methoden, denen eine gewisse Semantik zugrunde liegt. Wie die Implementierung dann aussieht, ist prinzipiell egal. Bei großen Datenmengen spielt die Art der Implementierung aber eben eine große Rolle. Eine Abfrage auf Typen einer diskreten Menge, wie z.B. IList, wenn auf IEnumerable operiert wird, ist grunsätzlich hässlich, da die Menge nicht ergänzbar ist, sonst wäre es wieder unperformant usw. Man kann zwar über Umwege gehen, aber das ist häufig viel Arbeit.
    Abstraktion ist find' ich elementarer Bestandteil jeder größerer Anwendung. Zwar ist damit ein Arbeitsaufwand verbunden, häufig auch etwas überdimensioniert, aber damit kann man Komponenten einzeln Testen und austauschen, um z.B. Flaschenhälse später herauszunehmen usw. Dass später die Bindung manuell erfolgen muss spielt häufig kaum eine Rolle. Wenn die Methode nicht virtuell ist, kann man direkt an den Methodenzeiger binden, während bei einer virtuellen Methode der Methodenzeiger im Typ festgehalten werden muss und ein indirekter Aufruf über diesen Zeiger erfolgt (somit eine späte Bindung, wird im IL als callvirt festgehalten). Der Unterschied macht sich zwar gering bemerkbar, aber das ist auf jeden Fall zu vernachlässigen, da es meist eh nicht auf einen konstanten Faktor ankommt - va. einen, der so klein ausfällt.

    Gruß
    ~blaze~

    ~blaze~ schrieb:

    Was ich eigentlich sagen wollte: Linq, Find, FindIndex usw. machen einem das Leben zwar einfacher, aber die Einfachheit führt halt häufig dazu, dass man sich ins Bein schießt.
    Also in diesem Falle wohl nicht. Die Konzepte "Find" und "FindIndex" sind in meinen Augen selbst Programmier-Pattern, da issis auf jeden Fall ein Schritt in die richtige Richtung, die Umsetzung der Pattern in List(Of T) zunächstmal ühaupt für sich zu nutzen.
    Das ist ja noch nichtmal Linq (weil Linq kann auch FindIndex)!

    Ausserdem ist das grauenhaft schnell - es ist ganz ausgeschlossen, dass da Optimierungsbedarf auftritt - vlt. iwann mal in einem ganz anners gelagerten Fall, und da kann man sich ja schon was ausdenken - vlt. was mit BinarySearch.

    nee, ich würde eher dazu tendieren, weitergehend diese Zuordnungs-Tabelle in ein typDataset zu integrieren, und Find und FindIndex-Äquivalente im typDataset kennenzulernen.
    Weils gibt ja sicher noch mehr Daten zu verarbeiten als nur diese Multiplikations-Faktoren, und's ist immer ganz geschickt, das gesamte Datenmodell auch in einem Dataset zu modellieren.
    Find und FindIndex waren natürlich losgelöst von Linq. FindIndex funktioniert vmtl. wie folgt:

    VB.NET-Quellcode

    1. Public Function FindIndex(ByVal match As Predicate(Of T)) As Integer
    2. For i As integer = 0 To Count -1
    3. If match(Me(i)) Then
    4. Return i
    5. Next
    6. Return -1
    7. End Function

    Analog läuft Find.
    Das ist eine sehr unperformante Art, das ganze zu behandeln, wenn viele Zugriffe bestehen. Eine Sortierung oder andere Zuordnungsart ist zwar unperformant, aber einmalig. Sämtliche späteren Operationen können durch z.B. binäre Suche oder lineares Sondieren gelöst werden und somit sind halt spätere Zugriffe wesentlich performanter.
    Ich bin jemand, der lieber vorsorgt, als nachsorgt, notfalls durch eine Abstraktion oder ausgelagerte Funktion ;). Ich verlass' mich nicht gern darauf, dass das für eine momentane Situation gut läuft. Wenn man später mal anders implementiert, läuft das oft nicht mehr so reibungslos.

    Gruß
    ~blaze~
    das Suchen in Datenbeständen ist eine ausgelutschte Wissenschaft, da sind die möglichen Komplexitätsgrade bekannt.

    Die niedrigste Komplexität hat die Suche in einem Dictionary: O:1
    das zweitbeste ist die binäre suche: O:lg(n)
    als letztes kommt die lineare Suche: O:n

    Nun erfordert aber Dictionary zum Suchen immer die Bildung eines HashCodes, und hat noch weiteren Overhead, sodass mans mal ausführlich testen müsste, ab wie vielen Elementen ein Dictionary anfängt, schneller zu sein als die olle dumme lineare Suche.
    Letztere hat zwar die höchste Komplexität, also die Anzahl der Suchschritte steigt linear mit dem Datenaufkommen, aber der einzelne Schritt ist wie gesagt grauenhaft schnell.

    Wie gesagt: Es lohnt sich nicht, hier von anfang an zu optimieren - Rules Of Optimization

    Wichtiger ist die ordentliche Strukturierung, dann ist späteres Optimieren auch kein Prob mehr.
    Wenn ich weiß, dasses iwo klemmt, wo linear gesucht wird, ist das recht schnell ausgetauscht durch was binäres oder gar ein Dictionary.

    aber glaub mir: Es ist irrelevant - es sei denn, du bist grade an Verarbeitung bewegter Bilder dranne oder sowas.
    Ich warte ungern unnötig lange auf Ergebnisse und wie gesagt, es muss ja nicht sofort implementiert werden, wenn man es abstrakt designt.
    Hashcodes kann man häufig relativ einfach ermitteln. Wenn man ein effizientes Hashverfahren findet, erhält man häufig schon eine wesentlich geringere Menge an Werten. Wenn man es dann "hybrid" als Hashset und Liste erzeugt und erst ab einer bestimmten Elementzahl erweitert, hat man auch noch geringere Datenmengen. Aber sinnvoll ist's erst, wenn man wirklich auf das Problem stoßt.
    Lineare Suche ist und bleibt relativ gesehen einfach langsam, binäre Suche relativ schnell.
    Was die Rules of Optimization angeht glaube ich, dass ich sie kenne und - zumindest für mich - so handhabe. Wichtig ist allerdings die Sicherstellung der Korrektheit nach der Optimierung und da ist der einfachste Weg, das über eine abstrakte Klasse zu lösen. Außerdem sollte man sich mMn. immer die Frage stellen, ob ein schnelles Hinschreiben über einem schnellen Programm steht. Häufig sind's halt einfach 10 Zeilen mehr oder teilweise sogar weniger als 10 und das wars. 10 Zeilen schreibe ich in ein paar Minuten und alles was über 5 Minuten rausgeht wird abstrakt dargestellt oder ausgelagert, sodass ein Austauschen problemlos möglich ist.
    Mit geringen Optimierungen kann man häufig schon extrem viel Zeit rausschinden und das ist's mir wert.

    Gruß
    ~blaze~