pl: Problem mit Arrayindex

problematische Seite

s. Thema. Das Problem tritt auf, wenn in der Anwendung 2 Einträge vorliegen und der erste gelöscht wird.

Es äußert sich so

[
  {
    "vname": "Vorname",
    "name": "Name",
    "idx": 1
  }
]

also, daß der Index auf idx:1 stehenbleibt und nicht zu 0 aktualisiert wird. Das wird er erst beim 2. Löschvorgang. Ich vermute einen Laufzeitfehler über dem Gesamtkonzept der Indexerei.

Sämtlicher JS Code liegt offen, vielleicht sieht jemand den Fehler. Das Problem ist weg, wenn der die render()Funktion nach dem Löschen 2x aufgerufen wird. Ist aber unschön.

Woran kanns liegen?

  1. problematische Seite

    Hallo pl,

    bitte poste das nächste mal den relevanten Code hier anstatt auf deiner Website.

    Woran kanns liegen?

    Du verwendest den falschen Operator um ein Array-Element zu löschen. delete entfernt eine Eigenschaft aus einem Objekt.

    Du suchst slice, splice oder filter, je nachdem, was du genau tun willst.

    LG,
    CK

  2. problematische Seite

    Tach!

    Das Problem tritt auf, wenn in der Anwendung 2 Einträge vorliegen und der erste gelöscht wird.

    Dein Code arbeitet beim Löschen anders als erwartet. Du solltest dir das Array anschauen, nachdem du einen Eintrag gelöscht hast.

    Der Eintrag ist nämlich nicht vollständig weg und damit auch der Index nicht neu vergeben. Eine funktionierende Lösung arbeitet mit splice() statt delete. (Außerdem ist delete keine Funktion sondern ein Operator und braucht keine Klammern um den Operanden.)

    dedlfix.

  3. problematische Seite

    Hallo pl,

    ich persönlich halte die Idee, ein Attribut eines Datenobjekts nach einem Löschvorgang neu zu vergeben, nicht für gut. Eine ID ist eine ID und bleibt eine ID. Wenn ein neu angelegter User eine ID bekommen hat, dann sollte er die auch behalten. Und er sollte sie von hinzu() bekommen, nicht von render(). Das verletzt das SoC Prinzip.

    Ein Lösch-Button sollte

    • nicht die ID des Elements anzeigen, sondern ein Mülleimer-Symbol oder ein X. Man weiß ja sonst gar nicht was er tut. Ein Edit-Formular anzeigen, etwa?
    • die zu löschende ID als value-Attribut enthalten
    • type="button" haben wenn eine reine SPA-Funktion gewollt ist. Ohne type="button" ist es type="submit", d.h. ein <form> (was Du nicht hast) würde an den Server geschickt und ein korrekter Event-Handler müsste das verhindern. Das wäre eine Fallback-Strategie für deaktiviertes JavaScript
    • in Leserichtung als letztes kommen (meine ich jedenfalls). Die Insert-Zeile könnte an dieser Stelle den "Speichern" Button bekommen (ggf. mit einem Häkchen drin).
    • nicht einzeln mit onclick attributiert sein, statt dessen sollte man unobtrusiv mit addEventListener einen click-Handler auf die table legen und die Klicks schön hochblubbern lassen. Bei Eintreffen schnell event.target abfragen ob es ein Button ist und dann den value auswerten.

    Ein div#out um die table ist für deine Darstellung auch nicht nötig, die id kannst Du auf die Table legen wenn Du sie brauchst. Wenn's auf Vorrat für weitere UI Elemente ist, ok, dann will ich nichts gesagt haben.

    Die Vorbelegung der Eingabefelder mit "Name" und "Vorname" finde ich Käse. Nimm lieber ein placeholder-Attribut. Das reicht aber nicht als Beschriftung; ich denke, ein sehbehinderter User mit Screenreader bekommt die Eingabefelder mit den Table-Headern (welche vielleicht besser in einem thead stehen sollten) nicht zusammen. Eventuell wären visuell versteckte Labels nötig, damit Accessibility gewährleistet ist.

    Rolf

    --
    sumpsi - posui - clusi
    1. problematische Seite

      @@Rolf B

      Die Vorbelegung der Eingabefelder mit "Name" und "Vorname" finde ich Käse. Nimm lieber ein placeholder-Attribut.

      Nein, verdammtnochmal.

      Das reicht aber nicht als Beschriftung

      Eben. Eingabefelder sollten eine Beschriftung haben; Placeholder sind dann nicht nur überflüssig, sondern störend.

      ich denke, ein sehbehinderter User mit Screenreader bekommt die Eingabefelder mit den Table-Headern (welche vielleicht besser in einem thead stehen sollten) nicht zusammen.

      aria-labelledby?

      LLAP 🖖

      --
      „Wer durch Wissen und Erfahrung der Klügere ist, der sollte nicht nachgeben. Und nicht aufgeben.“ —Kurt Weidemann
      1. problematische Seite

        hallo

        @@Rolf B

        Eben. Eingabefelder sollten eine Beschriftung haben; Placeholder sind dann nicht nur überflüssig, sondern störend.

        Das Problem mit Placeholder

        • Placeholder hat selbst keine logische Funktion. Die Bedeutung beruht also allein auf der Erfassung des Inhalts.
        • Wie bei allen Attributen sind placeholder Inhalte nicht einfach übersetzbar.
        • Da die Information nur verfügbar ist, solange das input Element keinen Inhalt hat, darf der Inhalt nicht wichtig sein, oder dessen Bedeutung muss obsolet werden, sobald das Feld einen nicht leeren Inhalt hat.

        Es ist leicht absehbar, dass da wenige vernünftige Beschriftungen für placeholder übrig bleiben. Da wir bereits ein required attribut haben, dieses aber nur bedingt an den User kommuniziert wird, wäre es durchaus möglich, placeholder genau dafür (zusätzlich) zu verwenden, statt nur etwa mit :invalid Feld-Gestaltung zu arbeiten.

      2. problematische Seite

        @Gunnar Bittersmann

        Eben. Eingabefelder sollten eine Beschriftung haben;

        Kein Problem, nachgebessert.

        MfG

      3. problematische Seite

        Hallo Gunnar Bittersmann,

        Die Vorbelegung der Eingabefelder mit "Name" und "Vorname" finde ich Käse. Nimm lieber ein placeholder-Attribut.

        Nein, verdammtnochmal.

        Wobei das placeholder-Attribut im Vergleich zur Vorbelegung die bessere weniger schlechte Lösung ist.

        Bis demnächst
        Matthias

        --
        Pantoffeltierchen haben keine Hobbys.
    2. problematische Seite

      Hi,

      • die zu löschende ID als value-Attribut enthalten

      nein, sondern den String, der sich aus einer geeigneten Verschlüsselung (samt Salz und was sonst noch so dazugehört) der id ergibt.

      Sonst könnte jemand hergehen, und im Button der Reihe nach 1, 2, 3, ... einsetzen und den Löschrequest abschicken.

      Bei einer verschlüsselten ID ist die Wahrscheinlichkeit, durch "Hochzählen" oder ähnliches wieder einen gültigen zu einer ID entschlüsselbaren Wert zu treffen, deutlich geringer.

      cu,
      Andreas a/k/a MudGuard

      1. problematische Seite

        hallo

        Sonst könnte jemand hergehen, und im Button der Reihe nach 1, 2, 3, ... einsetzen und den Löschrequest abschicken.

        Tja, dann haben wir auch bei deiner Methode ein Problem.

      2. problematische Seite

        Hallo MudGuard,

        Sonst könnte jemand hergehen, und im Button der Reihe nach 1, 2, 3, ... einsetzen und den Löschrequest abschicken.

        Was im hier gezeigten Kontext nicht schlimm wäre weil's die eigenen Daten sind. Und schützen tut das auch nicht wirklich - wer die Seite soweit kapert kann auch das Array plätten.

        Relevant werden solche Überlegungen wenn die Daten zentral abgelegt werden, aber dann muss die ID auf dem Server vergeben werden sonst fliegt der Salzstreuer für alle sichtbar im JavaScript rum. Und es muss ein Security-Konzept geben, das mich nur die Daten löschen lässt die ich darf. Das geht dann ein paar Schuhgrößen über das hinaus, was PL in seinem Text eigentlich zeigen will.

        Eine ganz andere Sache ist mein guter Freund <script>alert("Huhu")<script> Müller. Er kann ja nichts dafür, dass seine Eltern ihn so genannt haben, aber wenn ich ihn in der Liste erfasse, bleibt das Anzeigefeld leer und es geht immer so ein komischer Dialog auf. Ich glaube, die Funktion xr() ist etwas zu blond - äh - blauäugig programmiert.

        Rolf

        --
        Be alert! The world needs more lerts.
    3. problematische Seite

      @Rolf B

      Ein div#out um die table ist für deine Darstellung auch nicht nötig,

      natürlich nicht, da hast Du völlig Recht. Das hätte jedoch die Konsequenz, das Template zu zerstückeln und das ist außerst unschön in der Handhabe. Da lassen wir doch besser alles zusammen und pflanzen es in ein unnötiges <div>.

      MfG

      1. problematische Seite

        Hallo pl,

        als Template-Container ist es natürlich absolut existenzberechtigt. Sorry, das hatte ich übersehen.

        Rolf

        --
        sumpsi - posui - clusi
        1. problematische Seite

          Hi @Rolf B

          was mich am meisten ärgert ist, daß sich delete in JS genauso bescheuert verhält wie in Perl: Es setzt die betroffenen Einträge auf null anstatt sie zu löschen.

          Warum heißt das dann delete, steckt da ein tieferer Sinn dahinter der mir bisher verborgen scehint!?

          MfG

          1. problematische Seite

            Tach!

            was mich am meisten ärgert ist, daß sich delete in JS genauso bescheuert verhält wie in Perl: Es setzt die betroffenen Einträge auf null anstatt sie zu löschen.

            Hat der CK doch schon geschrieben. Wenn man Dinge verwendet, wofür sie nicht gedacht sind, darf man sich über das Ergebnis nicht wundern. Und delete löscht sehr wohl Eigenschaften von Objekten raus und setzt sie nicht nur auf null. Array-Elemente sind aber keine Objekt-Eigenschaften.

            dedlfix.

            1. problematische Seite

              Hallo dedlfix,

              Array-Elemente sind aber keine Objekt-Eigenschaften.

              Äh, doch. Zumindest lässt JS sie so aussehen. Guckst Du mein Snippet das ich grad für PL geschrieben habe.

              Rolf

              --
              sumpsi - posui - clusi
              1. Hallo Rolf

                Array-Elemente sind aber keine Objekt-Eigenschaften.

                Äh, doch.

                Es sind Objekt–Eigenschaften. Allerdings gibt es hinsichtlich ihrer Definition einige Unterschiede im Vergleich zu den Eigenschaften gewöhnlicher Objekte. Darum werden Arrays im Kontext von ECMAScript auch als ‚exotische Objekte‘ bezeichnet.

                Etwas genauer:

                Sowohl gewöhnliche als auch exotische Objekte besitzen eine Reihe interner Methoden, die Operationen auf dem Objekt durchführen. Ein Beispiel für eine solche interne Methode wäre die Methode [[Delete]](). Wird diese Methode mit einem Eigenschaftsnamen als Argument aufgerufen, dann wird versucht die zugehörige Eigenschaft auf dem Objekt zu löschen. Ein weiteres Beispiel wäre die interne Methode [[PreventExtensions]](), die auf dem Objekt ein Flag setzt, das weitere Eigenschaftsdefinitionen verhindert.

                Neue Eigenschaften werden über die interne Methode [[DefineOwnProperty]]() zu einem Objekt hinzugefügt – und die Definition dieser internen Methode unterscheidet Arrays von gewöhnlichen Objekten. Das ist nötig, damit die Eigenschaft length bei Operationen auf den Elementen des Arrays entsprechend angepasst werden kann – und umgekehrt.

                Das heißt, wenn eine Eigenschaft hinzugefügt werden soll, deren Name (gegebenenfalls nach einer Coercion) einen numerischen Index darstellt, hier eine nicht negative Ganzzahl zwischen 0 und 2³² - 1, dann wird bei Arrays geprüft, ob der Wert größer oder gleich dem Wert der Objekteigenschaft length ist. In dem Fall, dass die Eigenschaftsdefinition nicht aus anderen Gründen scheitert (siehe zum Beispiel oben) und der neue Index größer gleich dem alten Wert dieser Eigenschaft ist, dann wird length auf den ermittelten Index + 1 gesetzt, nachdem die Eigenschaft für diesen Index hinzugefügt wurde.

                Mal als Beispiel:

                // Create array with 3 elements
                
                const array = [1, 2, 3];
                
                array.length; // 3
                
                
                // Define new property with name '9'
                
                array[9] = 4;
                
                array.length; // 10
                

                Hier wird ein Array mit drei Elementen initialisiert, oder anders ausgedrückt, mit drei Eigenschaften deren Name ein numerischer Index ist. Durch die folgende Zuweisung, deren Name ein Index größer dem Wert der Eigenschaft length ist, wird der Eigenschaftswert von length automatisch angepasst, so dass er weiterhin um 1 größer ist als der größte in dem Array verwendete Index. Hierbei ist zu beachten, dass eine solche Zuweisung nicht dazu führt, dass auch Indizes kleiner dem neu hinzugefügten Index als Eigenschaften auf dem Array definiert werden, wenn entsprechende Eigenschaften wie im obigen Beispiel zum Zeitpunkt der Zuweisung nicht existieren.

                // Check if assignment created more than one property
                
                array.hasOwnProperty(7); // false
                

                Die Zuweisung an einen Index echt größer dem Wert der Eigenschaft length führt also dazu, dass in dem Array Lücken entstehen, es also Indizes kleiner der Länge des Arrays gibt, für die keine Eigenschaften definiert sind. Wie damit umgegangen wird, hängt dann von den jeweiligen Operationen ab, die auf dem Array ausgeführt werden. Eingebaute Funktionen wie zum Beispiel map(), reduce() oder filter() führen vor der Ausführung der als Argument übergebenen Rückruffunktionen für ein Element des Arrays eine Prüfung durch, ob für den aktuellen Index überhaupt eine Eigenschaft definiert wurde. (Das wird mit der internen Methode [[HasProperty]]() geprüft, die für alle Objekte definiert ist.) Ist keine Eigenschaft definiert, wird der Index übersprungen. Andere Methoden wie zum Beispiel die Arraymethode fill führen keine solche Prüfung durch. Hier werden nicht vorhandene Eigenschaften angelegt und mit dem gewünschten Wert befüllt.

                Lücken innerhalb eines Arrays können auch entstehen, wenn die Eigenschaft length gesetzt wird, entweder explizit durch Zuweisung, oder implizit, beispielsweise als Folge eines Aufrufs von Array mit einem einzelnen Argument, das einen Index repräsentiert.

                Schauen wir uns hierzu ein Beispiel an:

                // That's why you should use the array initializer syntax
                
                const array = Array(10);
                
                array.length; // 10
                
                
                // No properties defined
                
                array.hasOwnProperty(5); // false
                

                In dem Beispiel oben wird durch den Aufruf der Funktion Array ein neues Array erzeugt. Auf den ersten Blick mag es vielleicht so aussehen, als ob dieses Array mit einem Element, nämlich der Zahl 10 initialisiert wird, aber das ist falsch. Da 10 eine nicht negative ganze Zahl innerhalb des für Arrayindizes definierten Wertebereichs ist, wird stattdessen der Wert der Eigenschaft length des neu erzeugten Arrays auf 10 gesetzt. Eigenschaften für die Indizes kleiner als 10 werden nicht angelegt.

                const array = [1, 2, 3, 4, 5];
                
                array.length = 3;
                
                array; // [1, 2, 3]
                

                Wird die Eigenschaft length auf einen Wert kleiner dem aktuellen Wert gesetzt, dann sorgt die Ausführung der internen Methode [[DefineOwnProperty]] dafür, dass alle Eigenschaften gelöscht werden, deren Name ein Index größer gleich dem neuen Wert von length ist.

                Gelöscht werden auch Eigenschaften, die dem delete–Operator übergeben werden. Hier unterscheiden sich Array–Eigenschaften nicht von Eigenschaften gewöhnlicher Objekte. Das bedeutet insbesondere, dass der Wert von length nicht automatisch angepasst wird, selbst wenn der Name der gelöschten Eigenschaft der größte Index des Arrays ist. Auch in diesem Fall entstehen also Lücken und beim Zugriff auf eine gelöschte Eigenschaft wird wie gewöhnlich der Wert undefined substituiert.

                const array = [1, 2, 3, 4, 5];
                
                
                // Remove element at index 2
                
                array.splice(2, 1);
                
                
                // Check array
                
                array; // [1, 2, 4, 5]
                
                array.length; // 4
                

                Wie hier im Thread bereits gesagt wurde, sollte zum Entfernen eines Elements aus einem Array (wobei das Array selbst verändert werden soll) also besser die Methode splice verwendet werden. Hier rücken Elemente mit einem Index größer dem gelöschten Index auf, sodass etwaige Lücken geschlossen werden – und der Wert der Eigenschaft length wird entsprechend angepasst.

                Viele Grüße,

                Orlok

          2. problematische Seite

            Hallo pl,

            es verhält sich exakt so wie definiert: Es entfernt eine skalare Variable, bzw. das Property eines Objekts.

            Das Problem ist die Zwitter-Eigenschaft Array-Objekt; alle numerischen Array-Indexe sind auch Objekt-Properties.

            Guck Dir mal GENAU an, was Schritt für Schritt in folgenden Befehlen passiert:

            let a = [];
            console.log(Object.getOwnPropertyNames(a).toString());
            a[3] = 47;
            console.log(Object.getOwnPropertyNames(a).toString());
            a[1] = 17+4;
            console.log(Object.getOwnPropertyNames(a).toString());
            delete a[1];
            console.log(Object.getOwnPropertyNames(a).toString());
            

            Rolf

            --
            sumpsi - posui - clusi
            1. problematische Seite

              @Rolf B

              es verhält sich exakt so wie definiert: Es entfernt eine skalare Variable, bzw. das Property eines Objekts.

              delete arr[0] setzt den ersten Eintrag auf null. Wo bitte wird da was entfernt bzw. gelöscht!?

              MfG

              1. problematische Seite

                Hallo pl,

                let a = [1,2,3];
                delete a[0];
                if (a[0] === null) 
                   alert("Du Null!");
                else
                   alert("Denxte!");
                

                Bei mir (Chrome 71) kommt "Denxte!".

                Update: In Firefox, Edge und IE 11 auch. Du wirst Doch wohl nicht mit dem == Operator getestet haben?

                JavaScript - die Sprache die null und undefined unterscheidet.

                Rolf

                --
                sumpsi - posui - clusi
                1. problematische Seite

                  @Rolf B

                  let a = [1,2,3];
                  delete a[0];
                  if (a[0] === null) 
                     alert("Du Null!");
                  else
                     alert("Denxte!");
                  

                  So siehts aus:

                  [
                    null,
                    2,
                    3
                  ]
                  

                  MfG

                  1. problematische Seite

                    Hallo pl,

                    So siehts aus:

                    [
                      null,
                      2,
                      3
                    ]
                    

                    Selbst der NN4 hat das schon richtig gemacht. So siehts aus:

                    Beachte das „empty.“

                    Wenn es bei dir anders aussieht, dann benutzt du einen wirklich kaputten Browser oder du machst etwas anderes als du uns hier erzählst.

                    LG,
                    CK

                  2. problematische Seite

                    Hallo pl,

                    mit welchem Code erhältst Du diese Ausgabe? Mit arr.toString() jedenfalls nicht…

                    Hast Du mein Snippet ausgeführt? Was wurde alerted?

                    Rolf

                    --
                    be alert - the world needs more lerts!
                    1. problematische Seite

                      @Rolf B

                      zum Dumpen benutze ich JSON.stringify (wie auch auf der verlinkte Seite). Das zeigt auch die null nach einem delete und das ist nicht nur interessanter als die Prosa der Browser (empty, freie Position, o.ä.) sondern programmiertechnisch relevant.

                      MfG

                      1. problematische Seite

                        Hallo pl,

                        zum Dumpen benutze ich JSON.stringify

                        JSON.stringify muss den Index des nächsten Elements erhalten und füllt deshalb mit null auf.

                        Das zeigt auch die null nach einem delete und das ist nicht nur interessanter als die Prosa der Browser (empty, freie Position, o.ä.) sondern programmiertechnisch relevant.

                        „Programmiertechnisch relevant“ ist, was dir die Introspection-Tools deiner Umgebung zeigen. Also typeof, console.log und so. Was beim serialisieren passiert, hat ggfls ganz andere Hintergründe - siehe oben.

                        LG,
                        CK

                        1. problematische Seite

                          JSON.stringify muss den Index des nächsten Elements erhalten und füllt deshalb mit null auf.

                          Using delete may leave undefined holes in the array.

                          Was man sicher auch ohne JSON.stringify prüfen kann.

                          1. problematische Seite

                            Hallo pl,

                            Using delete may leave undefined holes in the array.

                            Das ist viel wahrer als man auf den ersten Blick meint - es hinterlässt Löcher mit Wert undefined.

                            Und der Nachsatz, den Du nicht zitiert hast, ist ein typischer Fall von w3fools - "use pop or shift instead". Hilft DIR natürlich gar nichts, du brauchst splice.

                            Zum Thema JSON und undefined siehe hier - es gibt in JSON kein Token undefined, obwohl es JSON heißt. Sehr ärgerlich - ein korrekter Roundtrip eines löchrigen Array wird damit verhindert.

                            Rolf

                            --
                            sumpsi - posui - clusi
                            1. problematische Seite

                              hi @Rolf B

                              JSON.stringify als Dumper ist für viele Zwecke ausreichend. Und ob mein Array Elemente enthält die null sind oder undefined ist für die Anwendung irrelevant: Weder das Eine noch das Andere ist zulässig.

                              Aber vielleicht läuft mir ja doch einmal in meinem Leben eine Anwendung über den Weg wo der Unterschied zwischen null und undefined eine Rolle spielt.

                              MfG

  4. problematische Seite

    @@pl

    Woran kanns liegen?

    An deinem falschen Konzept.

    Wenn das JSON in ein Array umgewandelt wird, dann hat jedes Item seinen Index. Eine Durchnummerierung in den JSON-Daten ist völlig überflüssig.

    Wenn jeder Eintrag noch eine eindeutige ID haben soll, dann eine unveränderliche – wie Rolf schon sagte. Wenn du einen deiner Oldtimer bei Sotheby’s in bare Münze umrubelst, bekommen die anderen in der Garage ja auch keine anderen Kennzeichen.

    LLAP 🖖

    --
    „Wer durch Wissen und Erfahrung der Klügere ist, der sollte nicht nachgeben. Und nicht aufgeben.“ —Kurt Weidemann
    1. problematische Seite

      @Gunnar Bittersmann

      Woran kanns liegen?

      An deinem falschen Konzept.

      Das Konzept ist völlig in Ordnung. Mein Denkfehler lag darin, zu übersehen, daß die zum Löschen markierten Einträge ja auch unter den Arrayindex fallen.

      Wenn das JSON in ein Array umgewandelt wird, dann hat jedes Item seinen Index. Eine Durchnummerierung in den JSON-Daten ist völlig überflüssig.

      JSON hat mit der Anwendung gar nichts zu tun. JSON dient hier nur zur Veranschaulichung der Datenstruktur.

      Wenn jeder Eintrag noch eine eindeutige ID haben soll, dann eine unveränderliche – wie Rolf schon sagte.

      Ja, natürlich, so kann man es auch machen. Aber meine Demo soll ja gerade zeigen, wie ein ohnehin vorhandener Arrayindex genutzt werden kann: Zur direkten Adressierung.

      MfG