ebody: javascript map() - auf bestimmte Elemente von value zugreifen

problematische Seite

Hallo,

ich möchte auf bestimmte Elemente von value zugreifen:

const items = movies.filter(value => value.row === 1).map(value => value[0]);

value[0] gibt undefined zurück. Kann man in einer anderen Schreibweise auf das erste, zweite... Element von value zugreifen?

Ziel ist ein neues Array zusammen zu stellen:

// movies[] durchlaufen und dabei nach row filtern
for (i = 0; i < movies.length; i++) {
 const items = movies.filter(value => value.row === i).map(value => ({movie:value[0].content,medium:value[1].content}));
}

Zum Hintergrund der Frage:

/**
 * movies[] ist eine verkürtze Darstellung eines Google Spreadsheet Arrays
 * Die Inhalte aus dem Array sollen in einem neuen Array je Datensatz (row) zusammengefügt werden, um dieses neue Array für verschiedene Funktionen besser/einfacher nutzen zu können.
 */

const movies = [
    {
      content: 'Batman Begins',
      col: 1,
      row: 1
    },
    {
      content: 'Blu Ray',
      col: 2,
      row: 1
    },
    {
      content: 'The Dark Knight',
      col: 1,
      row: 2
    },
    {
      content: 'Blu Ray',
      col: 2,
      row: 2
    }  
];

// movies[] durchlaufen und dabei nach row filtern
for (i = 0; i < movies.length; i++) {
  // const items = movies.filter(value => value.row === i).map(value => value);
  const items = movies.filter(value => value.row === i).map(value => value[0]); // [undefined, undefined]
  // const items = movies.filter(value => value.row === i).map(value => value[0].content); // Cannot read property 'content' of undefined
  // const items = movies.filter(value => value.row === 1).map(value => ({movie:value[0].content,medium:value[1].content})); // Cannot read property 'content' of undefined
  
  console.log('items', items); 
}

Gruß ebody

  1. problematische Seite

    Tach!

    ich möchte auf bestimmte Elemente von value zugreifen:

    Es wäre lesbarer, wenn du sprechende Namen verwendest statt dem generischen "value". In dem Fall wäre "movie" ein guter Name, weil das ein Element des movies-Array darstellt. Das gilt sowohl für filter() als auch für map().

    const items = movies.filter(value => value.row === 1).map(value => value[0]);
    

    Und damit würde man sehen, dass movie kein Array ist und der Zugriff auf [0] nicht sinnvoll ist.

    value[0] gibt undefined zurück. Kann man in einer anderen Schreibweise auf das erste, zweite... Element von value zugreifen?

    Da stellt sich dann die Frage, was das erste, zweite... Element eines movie sein soll. Oder aber: auf was konkret möchtest du eigentlich zugreifen?

    dedlfix.

    1. problematische Seite

      Hi,

      hattest du den kompletten Code gesehen und das Array movies[]? Ich weiß halt nicht genau, was value alles enthält und wirklich ist. Wenn ich items ausgebe, bekomme ich ein neues Array mit den ersten beiden Elementen aus movies[].

      const items = movies.filter(value => value.row === 1).map(value => value);
      console.log('items', items);
      

      Daher gehe ich davon aus, das value genau dieses neue Array ist (ist dem so?) und man auf die Elemente dieses neuen Arrays auch direkt zugreifen kann:

      Beispiel:

      const items = movies.filter(value => value.row === 1).map(value => value[0]);
      console.log('items', items);
      

      Was aber so leider undefined zurückgibt.

      Gruß ebody

      1. problematische Seite

        Tach!

        hattest du den kompletten Code gesehen und das Array movies[]? Ich weiß halt nicht genau, was value alles enthält und wirklich ist.

        Das ergibt sich aus der Funktionsweise der Methoden Array.filter() und Array.map(). Beiden ist gemein, dass sie ein Array durchlaufen und jeweils ein Element davon an die Callback-Funktion übergeben. Somit ist dein "value" jeweils ein Eintrag aus dem movies-Array, mithin also ein movie.

        Wenn ich items ausgebe, bekomme ich ein neues Array mit den ersten beiden Elementen aus movies[].

        const items = movies.filter(value => value.row === 1).map(value => value);
        console.log('items', items);
        

        Daher gehe ich davon aus, das value genau dieses neue Array ist (ist dem so?)

        Nein. Zunächst einmal hast du da zweimal "value", das eine in der Callback-Funktion von filter() und das zweite in der Callback-Funktion von map(). Ich nehme an, du meinst in dem Satz das zweite "value". Auch das enthält jeweils nur einen Film und nicht das gesamte - nun gefilterte - Array. Um einzelne Elemente eines Arrays gezielt anzusprechen, brauchst du kein .map(), sondern greifst über die Index-Notation zu.

        const filteredMovies = movies.filter(movie => movie.row === 1);
        console.log('filteredMovies', filteredMovies);
        console.log('erstes Element', filteredMovies[0]);
        console.log('zweites Element', filteredMovies[1]);
        

        und man auf die Elemente dieses neuen Arrays auch direkt zugreifen kann:

        Ja, aber ohne .map().

        Beispiel:

        const items = movies.filter(value => value.row === 1).map(value => value[0]);
        console.log('items', items);
        

        Was aber so leider undefined zurückgibt.

        Ein Debugger ist ein wichtiges Werkzeug zum Erkenntnisgewinn beim Programieren. Die Browser haben einen eingebaut, man muss ihn nur nutzen. Zur Not gibt es das von dir schon verwendete console.log(), mit dem du auch sehen könntest, was "value" wirklich ist: .map(value => console.log(value)).

        dedlfix.

      2. problematische Seite

        Hallo ebody,

        Ich weiß halt nicht genau, was value alles enthält

        Anhand deines Beispiels: Doch. Du weißt genau, dass das ein Objekt mit drei Eigenschaften ist: content, row und col.

        Die filter-Methode kopiert diejenigen Einträge eines Arrays, für die die Callbackfunktion true liefert. Bei Dir ist der Callback (wenn ich Dedlfix' Anregung mit dem sprechenderen Namen aufgreife) dieser:

        movie => movie.row === 1

        D.h. das Ergebnis des filter-Aufrufs ist ein Array mit den Filmen Batman Begins und Blu Ray. Blauer Rochen? Nie gehört, ist der gut?

        Achso, das sind ja gar nicht alles Filme. Es sind Spreadsheet-Zellen und "Blu Ray" ist das Medium, auf dem "Batman Begins" vorliegt.

        Dann nenn das verd***te Array doch bitte cells und nicht movies!!1!!elf!1

        Nach dem Filter-Aufruf hast Du dieses Array:

        [
            {
              content: 'Batman Begins',
              col: 1,
              row: 1
            },
            {
              content: 'Blu Ray',
              col: 2,
              row: 1
            }
        ]
        

        und der map-Aufruf wird nun wiederum auf jeden einzelnen Eintrag darin angewendet. Zuerst auf die Namensspalte (col:1), dann auf die Medienspalte (col:2).

        Aber ich glaube, das willst Du nicht. Du möchtest das Filterergebnis zu einem einzigen Objekt zusammensetzen. Dafür ist map ungeeignet. Mit map wandelt man einen Array-Eintrag in einen neuen Wert um.

        Du möchtest statt dessen mehrere Array-Einträge zu einem Objekt aggregieren:

        {
           movie: "Batman Begins",     /* content für col:1 */
           medium: "Blu Ray"           /* content für col:2 */
        }
        

        und vermutlich hast Du in der Realität noch ein paar Spalten mehr.

        Deine Steuerschleife am Ende ist dann auch unpassend, denn

        for (i = 0; i < movies.length; i++) {
        }
        

        würde ja einen Durchlauf pro Spreadsheet-ZELLE und nicht pro Spreadsheet-ZEILE machen.

        Man kann das Problem mit der filter-Methode lösen, aber dann

        • benenne movies in allMovieCells um
        • erzeuge ein leeres Array movies (nicht items, bitte) für das Ergebnis.
        • finde zunächst den maximalen Wert für "row" in allMovieCells
        • Lass eine Schleife von 1 bis maxRow laufen
          • Filtere alle Zellen zu dieser Row-Nummer heraus. Das Array nenn ich mal cellsForOneMovie - kannst es anders nennen wenn Du willst...
          • Verteile die Werte aus dem Spalten auf die passenden Properties eines Objekts - das kann man abstrakt oder primitiv bauen...
          • Hänge dieses Objekt an dein movies Array an (push)

        Ergebnis:

        [
           {
             movie: "Batman Begins",
             medium: "Blu Ray" 
           },
           {
             movie: "The Dark Night",
             medium: "Blu Moray" 
           }
        ]
        

        Zum Verteilen kannst Du eine Hilfsfunktion bauen, die Dir aus einem Array von Zellen den Wert der Zelle mit einer bestimmten Spaltennummer heraussucht.

        let movie = {
           movie: getCellValue(cellsForOneMovie, 1),
           medium: getCellValue(cellsForOneMovie, 2)
        }
        
        function getCellValue(cells, col) {
           // das kriegst Du selber hin
        }
        

        Wenn Du viele Zellen für einen Film hast, ist das lästig. Man könnte auch ein Array aufbauen, das den Spaltennummern Namen zuordnet. Ein Dummy-Eintrag vorneweg, weil JS-Arrays ja mit 0 beginnen.

        const columnNames = [ '', 'movie', 'medium', 'erschienen', 'regisseur', 'prodKosten' ],
              maxColumn = columnNames.length - 1;
        
        let movie = { };
        for (let cell of cellsForOneMovie) {    // for..of nicht im Internet Explorer!
           if (cell.col >= 1 && cell.col <= maxColumn) {
              colName = columnNames[cell.col];
              movie[colName] = cell.content;
           }
        }
        

        Für diesen Weg brauchst Du kein Durchsuchen der Zellen nach Spaltennummern, du ordnest über columnNames direkt die Spaltennummern einem Propertynamen zu.

        Rolf

        --
        sumpsi - posui - obstruxi
        1. problematische Seite

          Hallo Rolf,

          Nachtrag: Wenn die Zellen zumindest nach row sortiert vorliegen (sortiert nach col ist nicht nötig) kann man auf die Filterei auch verzichten und mit einem Gruppenwechsel arbeiten.

          Das ist nur unwesentlich komplizierter (wenn man die Idee gerafft hat) und deutlich fixer, weil nicht für jeden Film das komplette Zellen-Array durchforstet werden muss.

          Rolf

          --
          sumpsi - posui - obstruxi
  2. problematische Seite

    Hallo ebody,

    vielleicht ist das interessant für dich:

    https://www.mediaevent.de/javascript/array-multidimensional.html

    Denn bei den Verschachtelungen und Neustrukturierungen, gibts zwar viele Wege, doch meist multidimensional. Daher der Link mal als Anregung.

    Gruss
    Henry

    --
    Meine Meinung zu DSGVO & Co:
    „Principiis obsta. Sero medicina parata, cum mala per longas convaluere moras.“