Nico R.: Array wird in JS nach JSON-Umwandlung zu Objekt

Hallo zusammen,

mein PHP Array aus diesem Beitrag, das ich per

let toreArray = JSON.parse('<?=json_encode($toreArray)?>');

in JS als Array weiter nutzen möchte, wird dort als Objekt erstellt. In der Objekt-Eigenschaft ist dann das Unterarray gespeichert, jedes der Unterarrays auch wieder als Objekt usw. Im Grunde so, wie es auch der JSON-String vorgibt:

{"0":[347,{"1":[10,0]}],"1":[344,{"1":[20,1],"2":[30,0]}],"3":[338,{"1":[30,0]}]}

Da mein ursprüngliches Script zur Weiterverarbeitung aber auf einem reinen Array aufbaut (u.a. mit forEach(), wäre es mir eigentlich lieb, dass das Array auch wieder also solches vorliegt. Gibt es einen einfachen Weg, das zu bewerkstelligen? Ich habs mit Array.from(toreArray) versucht, aber da kommt am Ende nur ein leeres Array heraus.

Auf dem umgekehrte Weg, bei der Rückgabe des Arrays an PHP kann ich das Objekt-Array mit json_decode(json, true) wieder als lupenreines Array verarbeiten:

const toreStr = JSON.stringify(toreArray);
$ToreArray = json_decode($_POST['toreStr'], true);

Gibt es eine solche Funktion nicht auch in JS?

Schöne Grüße

Nico

  1. Lieber Nico,

    let toreArray = JSON.parse('<?=json_encode($toreArray)?>');
    

    in JavaScript ist alles ein Objekt, auch ein Array. Wenn Du ein natives Array-Objekt in JavaScript haben möchtest, dann sollte PHP den JSON-String mit eckigen Klammern umgeben und die Array-Schlüssel nicht als numerische Strings, sondern als echte Integer-Werte kodieren:

    [0:[347,[1:[10,0]]],1:[344,[1:[20,1],2:[30,0]]],3:[338,[1:[30,0]]]]
    

    Da ich nicht weiß, wie Du Dein Array überhaupt erstellst, weiß ich nicht, was json_encode als Daten zur Verarbeitung erhält. Aber genau da sollte die Ursache für Dein Problem liegen.

    Liebe Grüße

    Felix Riesterer

    1. Hallo Felix,

      …nicht als numerische Strings, sondern als echte Integer-Werte kodieren:

      [0:[347,[1:[10,0]]],1:[344,[1:[20,1],2:[30,0]]],3:[338,[1:[30,0]]]]
      

      So hatte ich mir das auch vorgestellt.

      Da ich nicht weiß, wie Du Dein Array überhaupt erstellst, weiß ich nicht, was json_encode als Daten zur Verarbeitung erhält. Aber genau da sollte die Ursache für Dein Problem liegen.

      Es ist ein Array, das ich per $toreArray = [] erzeuge und dann mit Daten aus MySQL fülle. Wenn ich das Array per var_dump anzeige, sieht das so aus:

      array(3) {
        [0]=>
        array(2) {
          [0]=>
          int(347)
          [1]=>
          array(1) {
            [1]=>
            array(2) {
              [0]=>
              int(10)
              [1]=>
              int(0)
            }
          }
        }
        [1]=>
        array(2) {
          [0]=>
          int(344)
          [1]=>
          array(2) {
            [1]=>
            array(2) {
              [0]=>
              int(20)
              [1]=>
              int(1)
            }
            [2]=>
            array(2) {
              [0]=>
              int(30)
              [1]=>
              int(0)
            }
          }
        }
        [3]=>
        array(2) {
          [0]=>
          int(338)
          [1]=>
          array(1) {
            [1]=>
            array(2) {
              [0]=>
              int(30)
              [1]=>
              int(0)
            }
          }
        }
      }
      

      Ohne weitere Zwischenschritte ist die direkt folgende Ausgabe per json_encode() folgende:

      "{"0":[347,{"1":[10,0]}],"1":[344,{"1":[20,1],"2":[30,0]}],"3":[338,{"1":[30,0]}]}"
      

      Von den optionalen Parametern auf https://www.php.net/manual/en/function.json-encode.php sieht JSON_OBJECT_AS_ARRAY aus, als würde es machen, was ich will. Aber auch mit json_encode($toreArray, JSON_OBJECT_AS_ARRAY) bleibt der JSON-String unverändert.

      Schöne Grüße

      Nico

      1. Hi,

        "{"0":[347,{"1":[10,0]}],"1":[344,{"1":[20,1],"2":[30,0]}],"3":[338,{"1":[30,0]}]}"
        

        also wenn ich das richtig sehe, wird im JSON überall da ein Array mit [] erzeugt, wo im Original die keys von 0 an ohne Lücke aufsteigend sind - in allen anderen Fällen ein Object mit {}.

        Und Javascript macht dann aus den [] wieder ein echtes Array und aus den {} ein Object.

        Du müßtest also die Keys anpassen, daß sie aufsteigend ohne Lücke von 0 vorliegen, oder für die fehlenden Keys noch Werte (Dummies, z.B. wenn nur positive Zahlen erlaubt sind, -1) einfügen (und die dann bei der Verarbeitung wieder ignorieren.

        cu,
        Andreas a/k/a MudGuard

        1. Hallo Andreas,

          also wenn ich das richtig sehe, wird im JSON überall da ein Array mit [] erzeugt, wo im Original die keys von 0 an ohne Lücke aufsteigend sind - in allen anderen Fällen ein Object mit {}.

          Das war der entscheidende Hinweis. Mir war gar nicht aufgefallen, dass mein Array ja doch zwei index-Fehler hatte. Einmal stand ein Hochzähler in der while-Schleife in einer falschen Verzweigung (im Kommentar unten Fehlerkorrektur #2) und ein anderer hat bei 1 angefangen zu zählen, statt bei 0 (im Kommentar unten Fehlerkorrektur #1). Nach der Korrektur sieht das Array jetzt so aus:

          array(3) {
            [0]=>
            array(2) {
              [0]=>
              int(347)
              [1]=>
              array(1) {
                // Fehlerkorrektur #1:
                [0]=>
                array(2) {
                  [0]=>
                  int(10)
                  [1]=>
                  int(0)
                }
              }
            }
            [1]=>
            array(2) {
              [0]=>
              int(344)
              [1]=>
              array(2) {
                // Fehlerkorrektur #1:
                [0]=>
                array(2) {
                  [0]=>
                  int(20)
                  [1]=>
                  int(1)
                }
                // Fehler 1
                [1]=>
                array(2) {
                  [0]=>
                  int(30)
                  [1]=>
                  int(0)
                }
              }
            }
            // Fehlerkorrektur #2:
            [2]=>
            array(2) {
              [0]=>
              int(338)
              [1]=>
              array(1) {
                // Fehlerkorrektur #1:
                [0]=>
                array(2) {
                  [0]=>
                  int(30)
                  [1]=>
                  int(0)
                }
              }
            }
          }
          

          Und so jetzt der saubere JSON-String für JS:

          "[[347,[[10,0]]],[344,[[20,1],[30,0]]],[338,[[30,0]]]]"
          

          Danke wieder einmal für eure Hilfe :-)

          Schöne Grüße

          Nico

          1. Lieber Nico,

            dass mein Array ja doch zwei index-Fehler hatte.

            weia, wer denkt denn an sowas? Aber schön, dass Dein Problem gelöst ist.

            Liebe Grüße

            Felix Riesterer

            1. Rolf und Andreas 😜 Ich nicht, obwohl ich tausendmal drüber geguckt hatte. Schwacher Prozessor vermutlich…

              Schöne Grüße

              Nico

              1. Hallo Nico,

                das hab ich nicht für einen Fehler gehalten, sondern für eine Eigentümlichkeit deiner Datenstruktur.

                Rolf

                --
                sumpsi - posui - obstruxi
                1. Hi,

                  das hab ich nicht für einen Fehler gehalten, sondern für eine Eigentümlichkeit deiner Datenstruktur.

                  ich auch. Aber dann hab ich gesehen, daß genau das den Unterschied zwischen [] und {} gemacht hat im erzeugten JSON.

                  cu,
                  Andreas a/k/a MudGuard

      2. Lieber Nico,

        entweder Du kodierst den JSON-String selbst (keine gute Idee), oder Du wertest die Daten in JavaScript entsprechend aus: [jsFiddle] (in die Browser-Konsole schauen).

        Liebe Grüße

        Felix Riesterer

        1. Hi Felix,

          danke dir, das wäre dann mein Notanker gewesen. Aber es ließ sich ja zum Glück doch sauber lösen.

          Schöne Grüße Nico

      3. Hallo Nico,

        es tut mir leid, aber: Nein, das ist für JSON kein Array.

        Wie die Railroad-Diagramme auf json.org bzw. im ECMA 404 Standard zeigen, ist es in einem Array nicht zulässig, Indizes anzugeben. Es ist auch nicht zulässig, Einträge wegzulassen. Daraus folgt:

        Ein Array, das aus einem JSON-String erstellt wird, hat fortlaufende Indizes. Deins nicht, es hat die Indizes 0, 1 und 3.

        Es gibt keine Möglichkeit, den Index des ersten Arrayeintrags festzulegen. Das bedeutet, dass JSON.parse diesen Index selbst festlegen muss. In JavaScript ist es tatsächlich erlaubt, in einem Array-Literal Einträge zu überspringen und damit Indexe wegzulassen. [1,2,,3] ist in JavaScript erlaubt. Aber nicht in JSON. Und deswegen beginnen aus JSON erstellte Arrays immer bei Index 0.

        PHP und JavaScript haben die Möglichkeit, "sparse arrays" zu erstellen. Das sind Arrays, bei denen nicht alle Indizes belegt sind. Diese Arrayform ist in JSON nicht darstellbar. Eine Datenstruktur, deren Schlüssel nicht fortlaufende Zahlen sind, ist in JSON zwangsweise ein Objekt. Ein JSON-Objekt ist definiert als Liste von Key-Value Paaren. Und der Key ist ein String. Und ein String steht in doppelten Anführungszeichen. So ist es spezifiziert, etwas anderes wird von JSON.parse abgewiesen.

        Eine direkte Möglichkeit, dein sparse array aus PHP nach JavaScript zu übertragen, sehe ich nicht. Felix' Vorschlag mit der o2a-Funktion in seinem Fiddle ist ein gangbarer Workaround, er hat aber mutmaßlich einen Fehler drin: Man muss "object" == typeof o[key] abfragen, denke ich, sonst passiert die Rekursion nicht wie gewünscht.

        PHP hat diesen "associative" Schalter bei json_decode, den gibt's in JavaScript nicht. Weil es in JavaScript assoziative Arrays so nicht gibt. Es gibt Arrays. Das sind exotische Objekte, in dem Sinne, dass sie eine besondere Verhaltensweise haben, nämlich die length-Eigenschaft. Und sie haben den Methodensatz von Array.prototype, der die Existenz von length voraussetzt. Das kannst Du nur durch Umkopieren sinnvoll adaptieren.

        Rolf

        --
        sumpsi - posui - obstruxi
        1. Hallo Rolf,

          ja, das wars. Da haben sich unsere Antworten zeitlich überschnitten. Das ist quasi die theoretische Unterfütterung des ganzen Dilemmas. So ists auch logisch besser nachvollziehbar.

          Ich gerate mit den Arrays in JS und PHP immer wieder durcheinander, wenn ich die z.B. in JS versuche per arrayname[] = wert zu füllen. Das finde ich in PHP in jedem Fall "bequemer". Und nun kam hier auch noch eine Besonderheit von JSON dazu. In diesem Fall wars aber gut, weil ich so zumindest gemerkt habe, dass mein Array nicht sauber erstellt war.

          Danke für die Nachhilfe :-)

          Schöne Grüße

          Nico

          1. Hallo Nico R.,

            PHP:

            arrayname[] = wert
            

            JavaScript:

            arrayname.push(wert)
            

            Rolf

            --
            sumpsi - posui - obstruxi
            1. Ja. Andererseits funktioniert das hier:

              arrayname[0] = wert
              

              Ich weiß inzwischen auch warum, aber wenn mans nicht verinnerlicht hat, ist es tendenziell doch verwirrend.

              Ich meine kürzlich irgendwo gelesen zu haben, dass push() ungünstig ist, sofern man beim Anlegen des Arrays nicht die Länge definiert, weil dann Speicher angezapft werden muss... Irgendetwas in der Art? Kann das sein? In der Praxis vermutlich kein so großes Problem.

              In meiner Arbeit brauche ich in der Regel eh die volle Kontrolle über meine Arrays und lege die keys über Zählvariablen manuell an.

              Schöne Grüße

              Nico

              1. Hallo Nico R.,

                Ich meine kürzlich irgendwo gelesen zu haben, dass push() ungünstig ist, sofern man beim Anlegen des Arrays nicht die Länge definiert, weil dann Speicher angezapft werden muss

                Das ist immer so, wenn man Arrays dynamisch aufbaut. Wenn Du VORHER weißt, wieviele Werte rein müssen, kannst Du das mit Array(4711) direkt angeben. Wenn Du mit einem leeren Array startest, dann ist es egal, ob Du der Reihe nach an arr[0] bis arr[4710] zuweist oder einen push nach dem anderen machst. Sag ich mal so aus der Hüfte geschossen, ich müsste es erstmal messen.

                "Angezapft" ist vielleicht auch nicht ganz passend, Speicher anzapfen musst Du immer um ein Array anzulegen. Aber wenn Du die Länge vorher nicht weißt, kann JavaScript nur raten, und es rät natürlich erstmal zurückhaltend. Wird die erratene Länge überschritten, muss der Array-Speicher erweitert werden, was dann neu anlegen, umkopieren und alten Speicher freigeben bedeutet. Das kostet etwas Zeit und ist eine Belastung für den Heap, bis der Garbage Collector wieder aufräumt.

                In der Praxis ist das dann ein Problem, wenn die Arrays sehr groß werden. Bei 10 oder 20 Einträgen ist es ziemlich wurscht bzw. nur bei extrem engen Zeittoleranzen relevant - aber dann ist JavaScript die falsche Sprache, dann besser WebAssembly (was ein Monster für sich ist) oder gar nicht im Browser, sondern lokal und mit einer GC-freien und statisch allocierenden Sprache. Beispielsweise C++.

                Rolf

                --
                sumpsi - posui - obstruxi
    2. Hallo Felix,

      '[0:[347,[1:[10,0]]],1:[344,[1:[20,1],2:[30,0]]],3:[338,[1:[30,0]]]]'
      

      es wäre mir neu, dass JSON eine Array-Notation mit vorgegebenen Indizes unterstützt. Meinem Browser ist es auch neu, JSON.parse auf diesen String haut sich weg.

      Rolf

      --
      sumpsi - posui - obstruxi