Problem mit Arrayindex
pl
- javascript
2 Christian Kruse2 dedlfix0 Rolf B0 Gunnar Bittersmann0 pl
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?
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
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.
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
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
@@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 🖖
hallo
@@Rolf B
Eben. Eingabefelder sollten eine Beschriftung haben; Placeholder sind dann nicht nur überflüssig, sondern störend.
Das Problem mit Placeholder
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.
Eben. Eingabefelder sollten eine Beschriftung haben;
Kein Problem, nachgebessert.
MfG
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
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
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.
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
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
Hallo pl,
als Template-Container ist es natürlich absolut existenzberechtigt. Sorry, das hatte ich übersehen.
Rolf
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
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 aufnull
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.
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
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
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
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
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
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
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
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
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
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
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.
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
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
@@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 🖖
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