Data Collection-Variables / Maps in AutoIt - Status?

  • Hey,

    in einem Code habe ich ein sehr komplexes 2D-Array, das Ints, Strings und weitere Arrays enthält. Eig. wäre wahrscheinlich besser als "Klasse" aufgehoben, aber die OOP-Versuche bei Android scheinen ja nicht gefruchtet zu haben.

    Daher habe ich mal geschaut, was es in AutoIt noch so für Möglichkeiten gibt, das zu refaktorieren. Ich habe nur ziemlich alte Posts gefunden. Darunter eine gute Übersicht zu Alternativen zu Arrays, wozu ich dann ergänzend nur noch die "Map" gefunden habe.

    Ich möchte vor allem wegen der Lesbarkeit vom Array weg, weil es so groß geworden ist. So ein assoziativer Datentyp wäre wirklich praktisch.

    Maps hören sich da sehr passend an, zumal man auch Arrays ablegen kann und ich habe ein kleines, aber sehr altes Tutorial dazu gefunden und sogar eine kleine UDF, um den Umgang zu erleichtern (_ArrayDisplay ist echt wichtig zum Debuggen für mich, ein Äquivalent zu haben wäre schon super^^).

    Ich habe auch einen Thread gefunden, wie man Arrays mit Maps verbunden nutzen kann. Aber da werden auch Dinge genutzt, die im alten Tutorial nicht verwendet wurden.

    In den alten Threads ist viel die Rede davon, dass Maps nur Beta sind, das sollte sich ja mittlerweile geändert haben, oder?

    In der Auflistung der Datentypen von AutoIt ist es genannt. Aber wenn ich mir dann im Kontext einzelne Funktionen (z. B. MapKeys) anschaue, ist da noch eine "Experimental"-Warnung.


    Das lässt sich etwas ratlos zurück, wie ich da einsteigen kann. Wie ist denn der Status von Maps? Es gibt einige Neuerungen und ist nicht mehr in der Beta, aber es ist noch nicht "stabil"? Und neuere UDFs/Tutorials dazu gibt es auch nicht?

    Gibt es irgendwo eine Sammlung der genauen (aktuellen) Syntax und aller Funktionen, die es direkt für Maps gibt?

    Ich habe einen kleinen Teil zur Syntax in der Doku gefunden. Ist aber ohne ausführlichere Beispiele auch nur ein Anfang.


    Ich wäre auf jeden Fall dankbar über weiteren Input :).


    Und wie gesagt, mir gehts darum, Informationen zu bestimmten Objekten abzulegen. z. B. Möbelstücke, es kann mehrere geben (das ist die eine Dimension meines Arrays) und dann gibt es etliche Eigenschaften, wie Höhe, Breite, Gewicht, Anzahl der Füße, Name, usw., aber auch weitere nested Arrays, z. B. eine Liste der Teile mit ID / Name / Anzahl.

    Ich habe nicht mal große Anforderungen an die Performance, es wird nur langsam richtig lästig, sich für alles die IDs merken zu müssen: "Ok, brauche die Ersatzteilliste, das war... hmmm... ah, ID 5 und dann der Name steht in der... ah, 3..." Und mein Array ist halt noch viel größer ^^

    Oder sollte man bei Arrays bleiben und dann einfach viele Hilfsfunktionen für genau das Array machen und z. B. die Möbel in einem 1D-Array ablegen (oder einem simplen 2D-Array) und dann einfach Funktionen ala getHeight($aArray[0]), usw. erstellen, die dann einfach nur auf die richtige ID zugreifen?


    Ist etwas viel Text geworden, aber ich dachte, dass ich meine Funde zu Maps auch gleich zusammenfasse / ablege, falls jemand eine ähnliche Frage hat und Input sucht. :)

    Vielen Dank für jede Hilfe!

    3 Mal editiert, zuletzt von aSeCa (19. Februar 2024 um 11:20)

  • Richtige Klassen und richtige Structs gibt es in AutoIt nach wie vor nicht. (DllStructs zählen nicht, die muss man von Hand verschachteln, und wenn man Strings als Datentyp hat muss man immer char[] verwenden was oft etwas nervig ist)

    Maps verwende ich inzwischen quasi exklusiv, wenn es um "kleine Arrays", oder "Fake Klassen" geht. Mein Vorgehen ist dabei eigentlich immer wie folgt:

    AutoIt
    Global Enum $TYPE_VECTOR2
    
    Func Vector2($vx = 0, $vy = 0)
    	Local $_[]
    	$_[0] = $vx
    	$_[1] = $vy
    	$_[99] = $TYPE_VECTOR2
    	Return $_
    EndFunc

    Der Trick ist nun, dass du via Switch $_[99] aussortieren kannst welche "Typen" wie behandelt werden.

    Soweit ich das sehen kann sind Maps inzwischen stabil, haben keine Memory Leaks (zumindest keine die ich noch finden konnte, ich habe selbstverständlich danach gesucht), und zu allem Überfluss sind sie für Integer-Keys in den meisten Fällen schneller als Arrays (bis zu einer gewissen Größe).

    Von daher:
    - Mach dir eine 2D Map (Map of Maps). Die kann man via $Map[][] in 2D auch direkt lesen und schreiben und sie verhält sich bei Integer-Keys exakt wie ein 2D Array. Hat aber den Vorteil dass man Zeilen hinzufügen kann, während man ein Array nicht einfach erweitern kann. Ich persönlich verwende immer Integer-Keys und ein "Global Enum mit $Name, $ID, $Fuesse, $Teileliste" direkt zu Beginn. Maps "kann" man auch mit String-Keys verwenden, aber damit verwirre ich mich meistens eher selbst als dass es was bringt.
    - In die erste Zeile kommen wie üblich die Eigenschaften (Name, ID, Füße, Teileliste, etc)
    - Teilelisten kannst du auch als Map direkt dort ablegen und dann via "$Map[15][$TeileListe][0] = Erstes Teil" lesen und schreiben.
    - Noch ein Vorteil von Integer-Keys (wenn du dich daran hältst sie von 0 an aufsteigend zu verwenden, ohne Lücken): UBound kann verwendet werden statt MapKeys -> Eine Map mit aufsteigenden Integer-Keys kann exakt wie ein Array verwendet werden. (Edit: Das gilt natürlich nicht für "Fake-Klassen" bei denen in der [99] der Typ steht)

    Du müsstest natürlich sämtliche Funktionen dieser "Informationsliste" selbst schreiben. Fast alle davon sollten aber trivial sein.
    Falls deine Objekteliste 10.000+ Einträge hat ist diese Methode vermutlich zu langsam / zu aufwendig. Dann brauchst du vermutlich eine DB und damit kenne ich mich nicht aus :D
    Edit: Genau für diesen Fall hat AspirinJunkie vermutlich eine geeignete UDF.


    lg

    M

  • In den alten Threads ist viel die Rede davon, dass Maps nur Beta sind, das sollte sich ja mittlerweile geändert haben, oder?

    In den alten Threads war das auch so - es war nur ein Feature der Beta-Versionen.
    In irgendeinem der letzten Stable-Releases haben Maps auch Einzug in diese gefunden.
    Ganz aus dem Fenster lehnen möchte man sich hier jedoch nicht aber defacto ist die Nutzung von Maps mittlerweile so verbreitet, dass diese auch in Zukunft mit ganz großer Wahrscheinlichkeit drin bleiben.
    Wenn du AutoIt verwendest dann kannst du im Grunde also ziemlich beruhigt auf Maps setzen.

    Und wie gesagt, mir gehts darum, Informationen zu bestimmten Objekten abzulegen. z. B. Möbelstücke, es kann mehrere geben (das ist die eine Dimension meines Arrays) und dann gibt es etliche Eigenschaften, wie Höhe, Breite, Gewicht, Anzahl der Füße, Name, usw., aber auch weitere nested Arrays, z. B. eine Liste der Teile mit ID / Name / Anzahl.

    Klingt für mich spontan eher weniger nach Maps und Co. als mehr nach einer Datenbank.
    Insbesondere SQLite ist (fast) direkt in Autoit integriert, performant und lokal.
    Je mehr Infos und evtl. Beispieldaten du gibst, umso eher könnte man dir ein Beispiel für so eine Datenbank inklusive deren Verwendung in AutoIt mal zeigen.
    Auch interessant wäre wo die Daten herkommen und wie du sie Daten ins Programm bekommst.
    Evtl. hätte ich da von deiner Beschreibung her eine UDF, welche eher dem entspricht was du erreichen willst.

  • Danke schon mal für eure Inputs :) Auf jeden Fall gut zu wissen, dass man Maps bedenkenlos verwenden kann. Ich hoffe, da macht einer von den "Profis" irgendwann auch noch mal eine Übersicht / ein Tutorial zu. :)

    Die Idee mit der Datenbank hatte ich auch schon, aber kam mir dann doch etwas oversized vor. Wird im Durchschnitt eher bei einer 2-stelligen Anzahl an Einträgen bleiben. Aber die Details gehen sehr in die Breite, würde bei einem Refactoring wahrscheinlich auch 1-2 nested Arrays auflösen und sind dann vielleicht so in der Breite 50 Details je Eintrag.

    Neben "oversized" war mir selber gegenüber auch ein Gegenargument, dass der Umgang mit nested Arrays in Datenbanken wahrscheinlich schwierig ist? Oder sind das dann andere Tabellen? Maps fühlen sich auf jeden Fall etwas näher an dem an, was ich kenne (Arrays) und bin fast nur noch mit AutoIt unterwegs (OOP mit Java nur im Studium) und Hobby-Programmierer, daher beschränkte Erfahrungen ^^

    Datenquellen sind Eingabe über UI und Websiten parsen.


    Der Beispielcode von Mars sieht für mich danach aus, was ich mir in etwa vorgestellt habe, dieses Prinzip der "Fake Klassen"... Und generell ist die Idee von einem Global Enum so einfach und doch so schlau... Das würde mir ja auch mit meinem Array schon so sehr helfen!

    => Wie macht man das denn am geschicktesten? Wenn man mehrere Arrays (und nested Arrays) hat, ist vielleicht ja irgendwann auch unübersichtlich, welches Enum zu welchem Array gehört?

    Nach der Idee stellt sich mir auf jeden Fall die Frage, ob es sich für diesen Fall für mich lohnt, das mit Maps zu refaktorieren, oder ob das ENUM schon mein Hauptproblem mit der Nachvollziehbarkeit löst... Aber bin natürlich auch ein Freund davon, Dinge weiter zu optimieren.

    Vorteil der Map wäre dann ja, dass wenn ich die Nested Arrays auch zu Maps mache, dass ich direkt darauf zugreifen kann, Nachteil, dass ich teilweise die einfachen Funktionen von Arrays verliere. :/ (bis auf Ubound, wenn ich bei Integern bleibe^^)

    Du müsstest natürlich sämtliche Funktionen dieser "Informationsliste" selbst schreiben. Fast alle davon sollten aber trivial sein.

    Könntest du bitte nochmal erklären, was du damit meinst? Ist ja jetzt keine richtige Klasse, dass ich Getter und Setter brauche?! Welche Funktionen meinst du jetzt? Auslesen und Schreiben würden dann ja auch ohne "Funktion" an den entsprechenden Stellen passieren?!

  • Damit habe ich sowas gemeint wie _ObjectList_AddItem($mmList, $mItem), _ObjectList_GetItemByKey($mmList, $iFuesse), etc. Die üblichen Verdächtigen. Oder DB-Ähnliche Queries wie _ObjectList_GetAllItemsWith($mmList, $iKey, $xValue). (Den prefix 'mm' kann man auch weglassen. Der ist zur Übersicht, dass man nicht vergisst dass "List" eine "Map of Maps" ist. "x" steht für "Unbekannter Typ" und "i" für Integer)

    Noch ein Hinweis zu Maps (der eigentlich offensichtlich ist, aber der hat mich schon einige Nerven beim debuggen gekostet): MapRemove erzeugt Löcher.

    lg

    M

  • Ah, super. Danke! :)


    Ich habe auf jeden Fall grade Enums für mich entdeckt und versuche das auf jeden Fall als ersten Schritt. Aber ich werde danach auch etwas mit Maps experimentieren und das vllt. später dann noch umbauen :)


    Hast du vielleicht noch einen Tipp bzgl. der Notation bei Enums? In der AutoIt-Docu wird "e" für den Anfang der Variable genutzt, aber grade mit deinem Input zu Maps habe ich grade so im Kopf "wäre ja nett, wenn man erkennt, was da für ein Datentyp drin steckt"...

    Ich wäre jetzt so bei z. B. "$eHeight_i", "$eSpareParts_a", $eName_s", usw... Ist das sinnvoll oder hast du da vllt. noch einen Insights in deine Best Practices? :)

  • Ich bin bei Enums ganz altbacken und verwende Großbuchstaben und Unterstriche (auch wenn ich das im Fließtext nicht gemacht habe) ohne Datentyp (typisch für #Define in C).

    Hier wäre das also sowas wie:
    ...
    $OLIST_WIDTH, _ ; Spaltenindex in der Objektliste (die 2D Map) für das Feld "Breite"
    ...

    Das sieht dann zwar aus wie Code aus den 80ern (obwohl ich da noch nichtmal gelebt habe), ist für mich aber am besten Lesbar. Dann sieht man direkt was Globale Konstanten sind.

    lg

    M

  • Ich denke, das hier duerfte die meisten Verwendungszwecke von Maps abdecken:

    Dabei solltest du darauf achten, dass du immer beim ersten zuweisen eines Wertes den Key in [ ] angeben musst. Bei verschachtelten Maps fuer alle Keys, auch wenn der davor schon existiert (siehe $mData["value2"]["something"]).
    Ansonsten kannst du mit der Punkt schreibweise Werte auslesen und ueberschreiben. Also mit z.B. $mData.value = 1. Das hat den Vorteil, dass SciTe dir das beim Tippen als Vorschlag gibt und du dir nicht alle Keys auswendig merken musst (Was z.B. bei Mars Variablen der Fall ist.)

    MapAppend hab ich mal rausgelassen, weil ichs noch nie gebraucht habe... Bei ner Map geht es ja genau darum, dass ich Key-Value-Paare haben will... warum also mit Integers arbeiten, da nutz ich persoenlich lieber Arrays (auch wenn man es natuerlich als "dynamisches Array" nutzen kann, wie Mars bereits erwaehnte, dann macht s ggf. Sinn).

    Ich finde es tatsaechlich etwas unintuitiv, dass bei einem "For $val in $mData" die Values durchlaufen werden und nicht die Keys. In allen anderen Sprachen die ich kenne werden die Keys (oder KeyValue-Paar-Objekte) iteriert und man kann dann damit die Values abfragen. Hier muss man leider erst alle Keys als Array holen (MapKeys) und die dann iterieren, was das ganze etwas umstaendlich macht :(


    Ich hoffe, das hilft dir weiter,

    LG Kanashius

  • Danke Mars, habe das mal so übernommen und meine beiden größten ARRAYS mit ENUMS ausgestattet. Sind zwar etwas lang teilweise, aber es ist auf jeden Fall wesentlich leserlicher und macht es viel, viel einfacher!


    Danke auch für dein Beispiel, Kanashius. Die Punktschreibweise gefällt mir auch sehr gut. Auswendig muss man die Keys bei Mars Vorschlag ja nicht kennen, ich kann jetzt auch einfach den übergreifenden Teil des Namens eintippen und dann durch alle passenden Variablen scrollen. Ich habe grade etwas rumprobiert und bei der Lösung ist es doch auch so, dass SCITE nicht die möglichen Key anbietet, sondern nur die, die schon mal genauso in der Kombination irgendwo im Code sind, oder? Ich habe mal ganz oben noch $mData["value3"] = "Value 3" gesetzt und dann kam value3 nicht als Vorschlag, wohl, weil ich noch nirgends $mData.value3 so geschrieben hatte.

    Das ist etwas schade, dann würde sich das schon fast nach meiner alten Java-OOP-Erfahrung anfühlen :D

    Aber es ist auf jeden Fall platzsparender und auch sehr intuitiv. Ich werde mir das Beispiel auf jeden Fall auch nochmal genauer anschauen. Danke! :)

  • Hallo

    Jetzt lese ich mir den Beitrag schon zwei mal durch, sehe mir die Beispiele an und stehe immer noch auf dem Schlauch. Da ich aber immer durch die Beiträge hier viel lernen kann hatte ich mich gefragt ob das so was ich selbst vor einiger Zeit zusammen geschieben habe:

    oder bin ich da völlig falsch unterwegs?

    lg

    Racer

    PS: Sorry das ich da so in den Betrag reinplatze

  • Das liegt daran, weil es für diese Art von Problemen so viele Lösungen gibt :)
    Jetzt haben wir:

    - Verschachtelte Arrays (Indexbasiert)
    - Verschachtelte Maps (Indexbasiert)
    - Verschachtelte Maps (Stringkey basiert)
    - SQLite (DB basiert, ist teil des Standards in AutoIt)
    - JSON (Stringkey basiert, falls man es damit machen möchte, dann geht das auch, da gibts ne gute UDF)
    - Dict (Stringkey basiert, mit BuiltIn Funktionen, oDict)
    - Irgendwo habe ich neulich auch eine Table-UDF (für Tabellen) gesehen. Habe aber vergessen wo.

    lg

    M

  • Irgendwo habe ich neulich auch eine Table-UDF (für Tabellen) gesehen. Habe aber vergessen wo.

    Na gut, da es jetzt doch erwähnt wird, will ich trotz der Gefahr der Schleichwerbung und zusätzlicher Verwirrung noch meinen Ansatz hierfür, welcher in der >>TableData-UDF<< implementiert wurde vorstellen.
    Diese UDF arbeitet mit Objekten (Maps um genau zu sein), welche die Daten von den Headern trennen.
    Das macht am Ende das Arbeiten mit diesen Daten einfacher, da statt Indizes Attributnamen verwendet werden können.

    Am besten am Beispiel: Hier konvertieren wir ein 2D-Array in ein solches Objekt und lassen es uns anzeigen (es kann aber auch von Strings, CSV-Dateien usw. eingelesen werden):

    AutoIt
    #include "TableData.au3"
    
    ; Ausgangsdaten: 2D-Array mit Header-Elementen in der 1. Zeile (man kann auch einen eigenen Header definieren)
    Global $aArray[5][4] = [["Name", "Alter", "Gehalt", "verheiratet"], ["Max", 25, 5000.50, True], ["Anna", 30, 6000.75, False], ["Peter", 35, 7000.25, True], ["Lena", 28, 5500.50, False]]
    
    ; Konvertierung in ein Tabellenobjekt:
    $mData = _td_fromArray($aArray, True)
    
    ; Daten anzeigen
    _td_display($mData, "Beispiel", True)

    Nun kann man diese Daten mit den mitgelieferten Funktionen so verwenden, dass man einfacher mit ihnen arbeiten kann.


    Z.B. kann man auf die Attributdaten der Datensätze einfach mit deren Namen zugreifen:

    AutoIt
    ; Alle Datensätze durchgehen und Zugriff auf deren Attribute per Attributname
    For $mP in _td_toObjects($mData)
    	ConsoleWrite( $mP.Name &  " (" & $mP.Alter & "): " & $mP.Gehalt & "€" & @CRLF)
    Next
    
    
    ; oder Einzelzugriff auf die Datensätze per Array-Index:
    $aDaten = _td_toObjects($mData)
    MsgBox(0,"Einzelzugriff", $aDaten[2].Name & " (" & $aDaten[2].Alter & ")")


    Oder wenn in einer Spalte Werte stehen, welche als Primärschlüssel geeignet sind, dann kann man diese auch nehmen um auf die einzelnen Elemente zuzugreifen:

    AutoIt
    ; konvertiere in Map, deren Key ein Primärschlüsselattribut ist:
    $mTable = _td_toPrimaryKeys($mData)
    
    ; Greife auf die Datensätze über ihren Primärschlüssel zu:
    MsgBox(0, "Annas Gehalt", $mTable.Anna.Gehalt)


    Oder man filtert die Daten anhand ihrer Eigenschaften:

    AutoIt
    ; Daten filtern
    $mData = _td_filter($mData, "$x.Gehalt > 5999")
    
    ; gefilterte Daten anzeigen
    _td_display($mData, "Großverdiener", True)


    Oder man extrahiert sich einzelne Spalten anhand ihres Namens:


    Oder man verknüpft seine Tabellendaten mit Daten aus anderen Tabellen:


    Das ganze lässt sich dann auch fix in eine CSV-Datei schreiben oder als normales 2D-Array wieder weiterverwenden - ganz wie man möchte.
    So ich denke das war jetzt genug Werbung.

    Einmal editiert, zuletzt von AspirinJunkie (20. Februar 2024 um 11:51) aus folgendem Grund: Beispiel für _td_filter() hinzugefügt

  • Na gut, da es jetzt doch erwähnt wird, will ich trotz der Gefahr der Schleichwerbung und zusätzlicher Verwirrung noch meinen Ansatz hierfür, welcher in der >>TableData-UDF<< implementiert wurde vorstellen.

    Das ist doch keine Schleichwerbung AspirinJunkie . Wenn du tolle UDFs zu Verfügung stellst, sprich auch gern darüber (zumindest meiner Meinung nach). Davon können sooooo viele Leute profitieren und dafür ist doch das Forum, GitHub (Open Source) u.a. da 😀 .

    Also Danke dafür 🤝 .

    Mars Danke auch dir für die Auflistung in Post #11: Ist krass wie viele Lösungsansätze existieren. Wird einem (mir) erst bewusst wenn es nochmal aufgezeigt wird 😅 .

    Viele Grüße
    Sven