EnglishGitHub
teilweisepackages/safe-array/src

Safe Array

@ralphschuler/safe-array

Verarbeitet gepufferte Mutationen ohne wiederholte Adds oder geteilte Konstruktor-Arrays.

iterationdeferred mutationcollection

01 · Problem

Wofür braucht man das?

Direktes Hinzufügen oder Entfernen während forEach kann Elemente überspringen, doppelt besuchen oder die Schleifenlogik schwer vorhersagbar machen.

02 · Denkmodell

Das mentale Modell

Die Iteration sieht einen Snapshot. Mutation landet in Queues und wird nur an klaren Flush-Grenzen atomar übernommen.

Im Repository

Verarbeitet gepufferte Mutationen ohne wiederholte Adds oder geteilte Konstruktor-Arrays. Entfernen erfolgt wertbasiert für alle Treffer. Nebenläufige Threads und Isolation verschachtelter Iterationen werden nicht zugesagt.

03 · Kontrollfluss

Was passiert in welcher Reihenfolge?

  1. Add und Remove in getrennten Pending-Strukturen sammeln.
  2. Vor Iteration einen Flush durchführen und beide Queues leeren.
  3. Über einen stabilen Snapshot iterieren.
  4. Währenddessen gesammelte Änderungen erst nach der Schleife übernehmen.

04 · Bauteile

Die entscheidenden Verträge

SafeArray.add / remove / forEach / filterPuffert Änderungen und iteriert mit definierten Mutationsgrenzen.
length / isEmptyLiest effektive Inhalte einschließlich gepufferter Änderungen.

05 · Selbst bauen

Selbst implementieren

Definiere zuerst, ob Änderungen aus Callback N noch im selben oder erst im nächsten Durchlauf sichtbar werden.

  1. Konstruktorinput kopieren und Pending-Adds sowie Remove-Set anlegen.
  2. Flush übernimmt jede Queue genau einmal und leert sie sofort.
  3. Iteration arbeitet auf einer Kopie oder einem unveränderten Basispuffer.
Konzeptskizze · kann ausgelassene Helfer verwenden
class DeferredList<T> {
  private values: T[] = [];
  private adds: T[] = [];
  private removes = new Set<T>();

  add(value: T) { this.adds.push(value); }
  remove(value: T) { this.removes.add(value); }
  flush() {
    this.values = this.values.filter(value => !this.removes.has(value));
    this.values.push(...this.adds);
    this.removes.clear();
    this.adds = [];
  }
}

06 · Verifizieren

Was solltest du testen?

  • Ein Add erscheint nach beliebig vielen Flushes genau einmal.
  • Remove während Iteration folgt der dokumentierten Sichtbarkeitsregel.
  • Add und Remove desselben Objekts in einem Zyklus haben eine festgelegte Reihenfolge.

Selbst ausprobieren

Formuliere die erste Prüfaussage als fehlschlagenden Test, implementiere die kleinste Korrektur und ergänze einen Grenzfall.

Ein Add erscheint nach beliebig vielen Flushes genau einmal.

Ansatz vergleichen

Dein Test sollte beim gezielten Verletzen der Invariante fehlschlagen und nach der Korrektur wieder bestehen. Halte den Aufbau deterministisch und prüfe ein beobachtbares Ergebnis.

07 · Grenzen

Kompromisse und Stolperfallen

  • Snapshots sind klar und erzeugen O(n)-Kopien.
  • Referenzgleichheit genügt für Objekte nur bei stabiler Identität.
  • Deferred Mutation schützt Iterationen, verzögert aber sichtbare Zustandsänderungen.
Wichtig

Entfernen erfolgt wertbasiert für alle Treffer. Nebenläufige Threads und Isolation verschachtelter Iterationen werden nicht zugesagt.

08 · Weiterdenken

Quellcode und verwandte Ideen

yarn tsx packages/safe-array/examples/basic.ts