ebody: Objekte mit Array mischen

Hallo,

es handelt sich um einen Beispielcode.

Ich möchte eine XML Datei auslesen und die Werte in einem Objekt speichern. Einige "Knoten" der XML enthalten den gleichen Namen, die weiteren Elemente aber unterschiedliche Werte.

Beispiel XML:

...
<auto>
	<marke>dacia</marke>
	<farbe>rot</farbe>
	<preis>8.000€</preis>
</auto>
<auto>
	<marke>dacia</marke>
	<farbe>schwarz</farbe>
	<preis>11.000€</preis>
</auto>
<auto>
	<marke>dacia</marke>
	<farbe>blau</farbe>
	<preis>10.000€</preis>
</auto>
<auto>
	<marke>porsche</marke>
	<farbe>rot</farbe>
	<preis>50.000€</preis>
</auto>
<auto>
	<marke>porsche</marke>
	<farbe>schwarz</farbe>
	<preis>60.000€</preis>
</auto>
...

Ich möchte das Objekt so erstellen, dass ich auf das Objekt und die Marke zugreifen kann und mir alle Varianten der Marke gezeigt werden.

Um das zu testen habe ich diesen Code verwendet:

var objCar2 = {"dacia":[],"porsche":[]};
objCar2.dacia.push({"Farbe":"rot","Preis:":"8.000€"});
objCar2.dacia.push({"Farbe":"scwarz","Preis:":"11.000€"});
objCar2.dacia.push({"Farbe":"blau","Preis:":"10.000€"});
objCar2.porsche.push({"Farbe":"rot","Preis:":"50.000€"});
objCar2.porsche.push({"Farbe":"scwarz","Preis:":"60.000€"});
console.log("objCar2 dacia: ", objCar2.dacia);
console.log("objCar2 porsche: ", objCar2.porsche);

Die Ausgabe in der WebDev Console zeigt alles richtig an, aber

  1. Ist es überhaupt "erlaubt" Arrays in Objekte zu mischen?
  2. Habt Ihr eine bessere Lösung / Optimierungs Tipps?

Gruß ebody

  1. Tach!

    1. Ist es überhaupt "erlaubt" Arrays in Objekte zu mischen?

    Gibts Syntaxfehler? Ein Array ist in Javascript auch nichts weiter als ein spezielles Objekt.

    1. Habt Ihr eine bessere Lösung / Optimierungs Tipps?

    Dacia und Porsche sind keine Schlüssel sondern Werte. Mach lieber ein Array für Autos, und da liegen dann gleichberechtigt alle drin, egal welche Typen und Ausstattung sie haben. Auf Teile davon kann man mit Methoden wie filter() zugreifen.

    dedlfix.

    1. Hi,

      Syntaxfehler gab es keine.

      Wenn ich jetzt aber ein Objekt haben möchte und die Marke soll ein Schlüssel sein, wie kann man dem gleichen Schlüssel dann weitere Eigenschaften zufügen?

      var autos = {};
      autos["Dacia"] = {"Farbe":"rot","Preis:":"8.000€"};
      autos["Dacia"] = {"Farbe":"scwarz","Preis:":"11.000€"};
      autos["Dacia"] = {"Farbe":"blau","Preis:":"10.000€"};
      console.log("autos: ", autos["Dacia"]); // Gibt die zuletzt hinzugefügten Eigenschaften aus
      

      In diesem Beispiel würden die Eigenschaften immer überschrieben.

      Mich interessiert speziell, wie man es in so einem Fall schreiben müsste. Evtl. wäre ein Array sinvoller, aber wie müsste man es in diesem Fall machen?

      Gruß ebody

      1. Tach!

        Wenn ich jetzt aber ein Objekt haben möchte und die Marke soll ein Schlüssel sein, wie kann man dem gleichen Schlüssel dann weitere Eigenschaften zufügen?

        Man kann einer Eigenschaft nur einen Wert zuweisen. Wenn es mehrere sein sollen, muss da ein Container drumherum sein, also ein weiteres Objekt. Oder ein Objekt vom Typ Array, also ein einfach gesagt ein Array.

        Das war schon richtig so in deinem Beispiel vom Eingangsposting.

        dedlfix.

  2. Hallo ebody,

    grundsätzlich bist Du frei, ein Array zu erstellen, das Objekte enthält, deren Eigenschaften Arrays sind, worin sich wieder Objekte befinden... ad nauseam (oder insaniam, je nach dem...)

    Du erstellt deine Objekte mit dem JavaScript-Konstrukt „Objektliteral“. Das ist völlig ok, aber deine Art es aufzuschreiben ist ungewöhnlich.

    // Deins
       {"Farbe":"rot","Preis:":"8.000€"}
    
    // Mein Vorschlag:
       { farbe: "rot", preis: 8000 }
    

    Warum?

    • Objekteigenschaften sollten in JavaScript nach dem camelCase-Pattern[1] benannt sein, d.h. erster Buchstabe klein und wenn es ein zusammengesetzter Begriff ist, die Folgebegriffe groß. Beispiele: firstElement, getElementById
    • Eigenschaftennamen müssen nur in Anführungszeichen stehen wenn sie keinen erlaubten JavaScript-Namen bilden. Eine Eigenschaft "preis:" ist einfach nur umständlich zu handhaben, darum sollte der Doppelpunkt da raus
    • Zahlen sollte man als Zahlen speichern, wenn möglich. Ein Währungssymbol sollte getrennt gespeichert werden (sofern überhaupt Multiwährungsfähigkeit gebraucht wird), dann rechnet man einfacher mit den Preisen. Für die Ausgabe verwendet man dann eine Formatierungsfunktion (Intl.Numberformat, toLocaleString oder zur Not was handgemachtes)

    Ob es für deinen Zweck sinnvoll ist, pro Marke ein eigenes Array zu bilden, hat dedlfix bereits bezweifelt. Wenn Du die Marke mit ans Auto-Objekt hängst und nur ein Array bildest, kannst Du bei Bedarf dieses Array filtern (z.B. mit der filter-Funktion, die zum Array-Prototyp gehört, oder mit einer Generatorfunktion. Eine Vorsortierung in ein Array pro Marke dürfte nur in Ausnahmefällen sinnvoll sein (sehr viele Autos und verschiedene Marken, häufiges Iterieren und Filtern auf Marke).

    var autos = [
       { marke: "Dacia", farbe: "rot" , preis:  8000},
       { marke: "Dacia", farbe: "gelb", preis:  2000},
       { marke: "Dacia", farbe: "blau", preis: 17000},
       { marke: "Porsche", farbe: "silber", preis: 80000},
       { marke: "Mercedes", farbe: "pink", preis: 120000}
    ];
    
    // 1. Markenindex bilden (als Eigenschaften eines marken-Objekts)
    marken = { };
    for (let a of autos) {
       console.log("Register " + a.marke);
       marken[a.marke] = 1;
    }
    
    // 2. Gefundene Marken auflisten
    for (let m of Object.getOwnPropertyNames(marken).sort()) {
       console.log("Marke: " + m);
       for (let a of autos) {
          if (a.marke == m)
             console.log("   Farbe: " + a.farbe
                + ", Preis: "
                + a.preis.toLocaleString(undefined, { style:"currency", currency: "EUR"}))
       }
    }
    
    // for...of iteriert über die Elemente eines Arrays. 
    // Es gibt natürlich auch andere Möglichkeiten, das zu tun...
    

    Rolf

    --
    sumpsi - posui - clusi

    1. Andere Schreibkonventionen sind PascalCase, snake_case, kebab-case. Den Namen "Kebab Case" habe ich eben erst in der Wikipedia gesehen als ich eine Referenz zum casing suchte; Klassennamen in HTML folgen typischerweise dem kebab-case. ↩︎

    1. Hallo Rolf,

      vielen Dank für das Beispiel, es hilft mir weiter und zeigt mir eine gute Variante. Bitte deute meine Frage jetzt nicht so, dass ich Deinen oder Eure Beiträge irgendwie ignoriere. Ich probiere gerade einfach verschiedenes dies bzgl. aus und dabei stoße ich immer wieder auf neue Fragen.

      Eigenschaftennamen müssen nur in Anführungszeichen stehen wenn sie keinen erlaubten JavaScript-Namen bilden.

      Wenn man jetzt nach diesem Beispiel gehen würde

      var autos = {dacia:[],porsche:[]};
      autos .dacia.push({"Farbe":"rot","Preis:":"8.000€"});
      autos .dacia.push({"Farbe":"scwarz","Preis:":"11.000€"});
      autos .dacia.push({"Farbe":"blau","Preis:":"10.000€"});
      autos .porsche.push({"Farbe":"rot","Preis:":"50.000€"});
      autos .porsche.push({"Farbe":"scwarz","Preis:":"60.000€"});
      console.log("autos dacia: ", autos .dacia);
      console.log("autos porsche: ", autos .porsche);
      

      Und das Array (die Marke) soll dem Objekt über eine Variable hinzugefügt werden, wie müsste man das dann schreiben?

      // Ein leeres Objekt
      var autos = {};
      
      // Die Variable deren Wert im Objekt als Array (Name) gespeichert werden soll
      var varMarke = "dacia";
      
      // Soll ein Array als Wert hinzugefügt werden
      autos = {varMarke:[]};
      

      In dem Fall würde das Objekt ein Array mit dem Namen varMarke und nicht dacia enthalten.

      Gruß ebody

      1. Hallo ebody,

        var autos = {};
        var varMarke = "dacia";
        autos = {varMarke:[]};
        

        Dieser Code hat zwei Probleme. Erstens möchtest Du dem Objekt in autos eine Eigenschaft hinzufügen. Du ersetzt aber das ganze Objekt. Zweitens möchtest Du, wie schon erkannt, den Namen der Eigenschaft aus einer Variablen holen und nicht statisch setzen.

        Wenn Eigenschaftsnamen variabel sind, muss man mit der [] Notation arbeiten. Ein Objektliteral mit variablen Eigenschaftsnamen ist überhaupt nicht möglich (es sei denn, man arbeitet mit fiesen Tricks wie eval(), aber das gehört zu den ugly parts von JavaScript).

        So geht es:

        // Im Objektliteral
        var autos = { Opel: [] };
        // Hinzufügen von Eigenschaften mit statischen Namen in Punkt oder [] Notation
        autos.Mercedes = [];
        autos["Porsche"] = [];
        // Hinzufügen von Eigenschaften mit variablen Namen nur in [] Notation
        var marke = "Dacia";
        autos[marke] = [];
        

        Eine Art "Objekt-Merge" Operator, also sowas wie

        var autos = { porsche: [] };
        autos += { dacia: [] };        // KEIN JAVASCRIPT !
        

        gibt es in JavaScript nicht. Aber ES2015 führt hierfür die Object.assign Methode ein.

        Rolf

        --
        sumpsi - posui - clusi
    2. Tach!

      // 1. Markenindex bilden (als Eigenschaften eines marken-Objekts)
      marken = { };
      for (let a of autos) {
         console.log("Register " + a.marke);
         marken[a.marke] = 1;
      }
      

      In modernem Javascript kann man für marken ein Set nehmen, da hat man gleich was ordentliches zum Iterieren und muss nicht mit solchen Hilfskonstrukten arbeiten: Object.getOwnPropertyNames(marken). Zur Not kann man daraus auch ein richtiges Array machen: Array.from(the_set).

      dedlfix.

    3. hallo

      // 2. Gefundene Marken auflisten for (let m of Object.getOwnPropertyNames(marken).sort()) { console.log("Marke: " + m); for (let a of autos) { if (a.marke == m) console.log(" Farbe: " + a.farbe + ", Preis: " + a.preis.toLocaleString(undefined, { style:"currency", currency: "EUR"})) } }

      Toll. Ich kann zwar kein Chinesisch, aber Preise kann ich in einer mir unverständlichen Form publizieren. Wenn das rechtlich mal nicht schief läuft.

      1. Hallo beatovich,

        hättest Du dies hier besser gefunden? Oder eine Callback-in-Callback Orgie mit Array.prototype.foreach?

        // 2. Gefundene Marken auflisten
        var markennamen = Object.getOwnPropertyNames(marken).sort();
        for (var m=0; m<markennamen.length; m++) {
           console.log("Marke: " + markennamen[m]);
           for (var au=0; au<autos.length; au++) {
              if (autos[au].marke == markennamen[m])
                 console.log("   Farbe: " + autos[au].farbe
                    + ", Preis: "
                    + formatEuroPreis(autos[au].preis));
           }
        }
        
        function formatEuroPreis(preis) {
           // do it somehow
           return formatierterPreis;
        }
        

        Rolf

        --
        sumpsi - posui - clusi
        1. hallo

          Hallo beatovich,

          hättest Du dies hier besser gefunden? Oder eine Callback-in-Callback Orgie mit Array.prototype.foreach?

          // 2. Gefundene Marken auflisten
          var markennamen = Object.getOwnPropertyNames(marken).sort();
          for (var m=0; m<markennamen.length; m++) {
             console.log("Marke: " + markennamen[m]);
             for (var au=0; au<autos.length; au++) {
                if (autos[au].marke == markennamen[m])
                   console.log("   Farbe: " + autos[au].farbe
                      + ", Preis: "
                      + formatEuroPreis(autos[au].preis));
             }
          }
          
          function formatEuroPreis(preis) {
             // do it somehow
             return formatierterPreis;
          }
          

          Lass mich so antworten:

          Falls ich als Beamter dich auffordere, dich zu einem bestimmten Termin einzufinden, so werde ich den Termin gewiss NICHT nach deinem locale angeben, sondern nach einer Methode, die objektiv ist.

  3. Hi,

    selbstverständlich kann eine Eigenschaft mehrere Werte beinhalten. Z.B. Zubehörteile oder Sonderausstattungen. Verschiedene Farben oder Größen jedoch sind Variationen, so hat ein Artikel nur eine Farbe und eine Größe.

    MfG