JavaScript >> Javascript-Tutorial >  >> Tags >> map

Beispiele für Zuordnen, Filtern und Reduzieren in JavaScript

Ich weiß nicht, wie es Ihnen geht, aber es hilft mir nicht sehr, endlose Beispiele von foo durchzugehen und bar um herauszufinden, wie man eine Funktion einer Programmiersprache verwendet. Es gibt einige JavaScript/ES6-Konzepte, die als eigenständige Snippets schwer zu verstehen sind, ohne zu sehen, wie sie als Teil eines größeren Projekts funktionieren, daher werde ich in diesem Artikel ein Beispiel dafür behandeln, wie ich sie verwendet habe.

Kürzlich habe ich Chicago erkundet und verschiedene lokale Coffeeshops besucht, um in einem sanften, angenehmen Ambiente à la J.K. zu schreiben. Rowling. Nachdem ich aus Versehen eines besucht hatte, das mir nicht besonders gefiel, das ich aber vergessen hatte, dachte ich, es könnte ein lustiges kleines Projekt sein, alle Cafés, in denen ich schreibe, in einer Webkarte abzubilden.

Bei einer anderen Gelegenheit habe ich bei meinem letzten Job die Google Maps API verwendet, um mehrere Ladenstandorte für Restaurantkonzepte zu kartieren. Für dieses Projekt entschied ich mich für Leaflet, eine JavaScript-Bibliothek für interaktive Karten.

Hier ist das letzte Kartenprojekt, das ich erstellt habe:Cafétography

Für diesen Artikel können Sie entweder Ihre eigene Webkarte für etwas in Ihrer eigenen Nachbarschaft oder der Welt erstellen, die Sie verfolgen möchten, oder Sie können sich einfach meine Beispiele für map() ansehen , filter() und reduce() .

Voraussetzungen

  • Grundkenntnisse in HTML, CSS und JavaScript. Eine Überprüfung von JavaScript-Variablen und -Datentypen wird Ihnen die meisten JavaScript-Kenntnisse vermitteln, die Sie bis zu diesem Punkt benötigen.

Ziele

  • Das nächste Ziel ist die Verwendung von reduce() und map() in einem realen Beispiel (der Karte), um unseren Code zu vereinfachen und effizienter und trockener zu machen (D auf nicht R Wiederholen Sie Y uns selbst ). Wir verwenden reduce() um die Anzahl der Coffeeshops pro Stadtteil von Chicago zu erhalten, und wir verwenden map() um Daten aus einem externen JSON-Feed abzurufen und Standorte auf der Webkarte anzuzeigen.

Lassen Sie uns ein paar Probleme lösen.

Einrichten der Webkarte

Hier ist ein Beispielprojekt auf CodePen, das Sie verwenden können. Es wird lediglich eine HTML-Datei benötigt (index.html ) mit den erforderlichen CSS- und JavaScript-Leaflet-Dateien, die geladen werden in:

index.html
<link href="https://unpkg.com/[email protected]/dist/leaflet.css" />
<script src="https://unpkg.com/[email protected]/dist/leaflet.js"></script>

Ein HTML div mit einer ID von map :

index.html
<div id="map"></div>

Etwas grundlegendes CSS (style.css ), um die Karte anzuzeigen:

style.css
#map {
  height: 100vh;
  width: 100%;
}

Und dieses JavaScript (scripts.js ), um die Karte und die Kartendaten zu laden.

scripts.js
// Set the map variable
const myMap = L.map('map')

// Load the basemap
const myBasemap = L.tileLayer(
  'http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',
  {
    maxZoom: 19,
    attribution:
      '© <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>',
  }
)

// Add basemap to map id
myBasemap.addTo(myMap)

// Set view of the map
myMap.setView([41.939948, -87.650673], 12)

Sobald dies alles eingestellt ist, sehen Sie hier, wie Ihre in Leaflet geladene Webkarte aussehen wird.

Auch hier können Sie eine funktionierende Version des Ganzen direkt hier auf CodePen finden und forken, aber Sie müssen sie auf Ihren eigenen Server übertragen oder lokal daran arbeiten.

Hinzufügen von Standortmarkierungen mit JSON

Jetzt möchte ich damit beginnen, Orte als Markierungen zur Karte hinzuzufügen. Ich kann das einfach tun, indem ich eine Reihe von Variablen mit Breiten- und Längengrad erstelle und den addTo() verwende Methode. Angenommen, ich möchte El Meson, Wormhole und Ipsento zu meiner Karte hinzufügen. Ich beschränke mich auf drei, um es kurz und einfach zu halten. Hier ist, wie ich es machen würde, um zu beginnen.

scripts.js
const elMeson = L.marker([42.002439, -87.672339]).addTo(myMap)
const wormhole = L.marker([41.908415, -87.674605]).addTo(myMap)
const ipsento = L.marker([41.918639, -87.687247]).addTo(myMap)

Das scheint nicht so schlimm zu sein - ich muss nur für jeden neuen Marker einen neuen Eintrag hinzufügen. Es wird jedoch etwas ausführlicher, sobald ich anfange, ein Popup mit weiteren Informationen für jeden Standort hinzuzufügen.

scripts.js
const elMeson = L.marker([42.002439, -87.672339])
  .bindPopup(
    `
    <h2>El Meson</h2>
    <p><b>Neighborhood:</b> Rogers Park</p>
    <p><b>Ambiance:</b> Great!</p>
    <p><b>Flavor:</b> Great!</p>
    <p><b>Comments:</b> Great!</p>
    `
  )
  .openPopup()
  .addTo(myMap)

const wormhole = L.marker([41.908415, -87.674605])
  .bindPopup(
    `
    <h2>Wormhole</h2>
    <p><b>Neighborhood:</b> Wicker Park</p>
    <p><b>Ambiance:</b> Great!</p>
    <p><b>Flavor:</b> Great!</p>
    <p><b>Comments:</b> Great!</p>
    `
  )
  .openPopup()
  .addTo(myMap)

// And so on...

So sieht es mit den geladenen Markierungen und Popups aus.

Jetzt kann es mühsam werden, für jeden einzelnen Coffeeshop, auf den ich stoße, einen neuen hinzuzufügen. Es wäre viel einfacher, eine JSON-Datei zu erstellen und diese zu durchlaufen. Wenn Sie JSON noch nie zuvor verwendet haben, empfehle ich dringend, dieses JSON-Tutorial zu lesen, um alles von Grund auf neu zu lernen und etwas zu üben.

An dieser Stelle werde ich alle Markierungsinformationen aus scripts.js löschen und erstelle eine JSON-Datei mit einem Objekt, das ein Array mit Objekten aller meiner Kaffeehausstandorte enthält. Anfangs kann es etwas schwierig sein, den Überblick über all die eckigen und geschweiften Klammern zu behalten.

map.json
{
  "cafes": [{
      "name": "El Meson",
      "lat": 42.002439,
      "long": -87.672339,
      "neighborhood": "Rogers Park",
      "ambiance": "4/5",
      "flavor": "5/5",
      "comments": "Best cappuccino and croissant I've ever had."
    },
    {
      "name": "Wormhole",
      "lat": 41.908415,
      "long": -87.674605,
      "neighborhood": "Wicker Park",
      "ambiance": "5/5",
      "flavor": "4/5",
      "comments": "Cute ambiance with a Nintendo that actually works properly and the best games (including FF1!)."
    },
    {
      "name": "Ipsento",
      "lat": 41.918639,
      "long": -87.687247,
      "neighborhood": "Wicker Park",
      "ambiance": "4/5",
      "flavor": "5/5",
      "comments": "Really great spicy latte. Nice ambiance."
    }
  ]
}

OK gut. Jetzt haben wir alle Informationen zu jedem Standort – Name, Breitengrad, Längengrad, Nachbarschaft und zusätzliche Details – übersichtlich in einer JSON-Datei platziert. Wie bekommen wir nun diese JSON-Datei auf die Seite?

Wir werden kein jQuery verwenden – nur einfaches JavaScript – es ist also ein etwas komplizierterer Prozess. Ich werde für eine weitere Erläuterung des Codes auf So verwenden Sie JSON-Daten mit PHP oder JavaScript verweisen, aber hier erfahren Sie, wie wir unsere map.json öffnen und darauf zugreifen Datei.

scripts.js
// Make an XMLHttpRequest to the JSON data
const request = new XMLHttpRequest()
request.open('GET', 'map.json', true)

request.onload = function () {
  // Begin accessing JSON data here
  const data = JSON.parse(this.response)
}

request.send()

Wo es heißt Begin accessing JSON data here Hier beginnen wir mit der Arbeit mit den Daten aus map.json , die in data platziert wurde Variable. Stellen Sie sicher, dass map.json ist keine lokale Datei-URL. Siehe Informationen unten.

Verwenden von map() zum Durchlaufen von JSON-Daten

Zuvor haben wir für jeden Marker eine neue Variable erstellt und alle Informationen manuell in diese Variable eingefügt. Jetzt ziehen wir alle Idata aus JSON, und wir tun dies mit map() .

map() nimmt ein Array und erstellt ein neues Array mit dem Ergebnis einer Funktion für jedes Element im ursprünglichen Array. Beispielsweise könnten Sie ein Array von [1, 2, 3] erstellen und über map() eine Funktion anwenden die jede Zahl in einem Array um eins erhöht. Sie würden am Ende [2, 3, 4] erhalten .

Um die in cafes gefundenen Daten abzurufen in JSON verwenden wir den map() Methode auf data.cafes mit einer Funktion darin.

const cafes = data.cafes.map(function (cafe) {})

Ich werde diese Funktion einfach mit einer ES6-Pfeilfunktion umschreiben, um sie prägnanter zu machen.

const cafes = data.cafes.map((cafe) => {})

Um jetzt auf eine Eigenschaft aus dem JSON-Feed zuzugreifen, verwenden wir die Punktnotation für cafe Objekt. Also die erste Iteration von cafe.name gibt El Meson zurück , die zweite Wormhole , usw. Ich nehme hier nur denselben L.Marker Funktion von vorhin und Ersetzen aller statischen Werte durch dynamische Eigenschaften.

scripts.js
// Print cafe markers
const cafes = data.cafes.map((cafe) => {
  L.marker([cafe.lat, cafe.long])
    .bindPopup(
      `
        <h2>${cafe.name}</h2>
        <p><b>Neighborhood:</b> ${cafe.neighborhood}</p>
        <p><b>Ambiance:</b> ${cafe.ambiance}</p>
        <p><b>Flavor:</b> ${cafe.flavor}</p>
        <p><b>Comments:</b> ${cafe.comments}</p>
    `
    )
    .openPopup()
    .addTo(myMap)
})

In diesem Beispiel werden Vorlagenliteralzeichenfolgen verwendet, die Backticks verwenden und sich über mehrere Zeilen erstrecken sowie Variablen mit Escapezeichen im Gegensatz zur Verkettung enthalten können. Sehen Sie sich dies an, wenn Sie mit Template-Literalen nicht vertraut sind.

Hier ist die vollständige scripts.js Datei bis jetzt.

scripts.js
// Set the map variable
const myMap = L.map('map')

// Load the basemap
const myBasemap = L.tileLayer(
  'http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',
  {
    maxZoom: 19,
    attribution:
      '© <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>',
  }
)

// Add basemap to map id
myBasemap.addTo(myMap)

// Set view of the map
myMap.setView([41.939948, -87.650673], 12)

// Make an XMLHttpRequest to the JSON data
const request = new XMLHttpRequest()

request.open('GET', 'map.json', true)
request.onload = function () {
  // Begin accessing JSON data here
  const data = JSON.parse(this.response)

  // Print cafe markers
  const cafes = data.cafes.map((cafe) => {
    L.marker([cafe.lat, cafe.long])
      .bindPopup(
        `
          <h2>${cafe.name}</h2>
          <p><b>Neighborhood:</b> ${cafe.neighborhood}</p>
          <p><b>Ambiance:</b> ${cafe.ambiance}</p>
          <p><b>Flavor:</b> ${cafe.flavor}</p>
          <p><b>Comments:</b> ${cafe.comments}</p>
        `
      )
      .openPopup()
      .addTo(myMap)
  })
}

request.send()

Natürlich map() kann viel mehr als das, was wir in diesem Beispiel tun, aber es ist ein guter und praktischer Anfang.

Filter() verwenden, um übereinstimmende Werte zu finden

An diesem Punkt hatte ich eine Karte mit all meinen Markierungen erstellt. In unserem reduzierten Beispiel gab es drei Cafés, aber ich landete bei etwa einem Dutzend. Jedes Café befand sich in einem Viertel, und ich hatte ein paar Viertel, in denen es mehr als ein Café gab, das ich besucht hatte. Ich wollte zählen, wie viele Cafés ich in jedem Viertel besucht habe.

In unserem Beispiel haben wir zwei Viertel für die drei Cafés – eines im Rogers Park und zwei im Wicker Park. Bei so einem kleinen Beispiel wäre es natürlich sehr einfach, einfach „Rogers Park:1; Wicker Park:2“ zu schreiben, aber je länger es wird und je mehr Viertel und Cafés hinzukommen, desto größer wird der Bedarf um diesen Vorgang zu automatisieren.

Wir haben alle Daten, die wir brauchen, in der JSON-Datei, aber ich war mir nicht sicher, wie ich alle Instanzen kurz und bündig zählen sollte. Zuerst dachte ich darüber nach, filter() zu verwenden um herauszufinden, wie viele Instanzen jedes Nachbarschaftsnamens im gesamten Feed gefunden wurden. filter() wird ähnlich wie map() geschrieben und es erstellt auch ein neues Array, aber seine Funktion führt einen Test durch und filtert alles heraus, was nicht bestanden wird. filter() folgt der gleichen Syntax wie map() .

const individualNeighborhood = data.cafes.filter((cafe) => {})

Unten mache ich einen Test für jede Nachbarschaft, der alle Ergebnisse herausfiltert, die nicht mit meinem Test übereinstimmen.

const rogersPark = data.cafes.filter((cafe) => {
  return cafe.neighborhood === 'Rogers Park'
})

const wickerPark = data.cafes.filter((cafe) => {
  return cafe.neighborhood === 'Wicker Park'
})

Mit dieser Methode müsste ich ein Array von Objekten erstellen, die die Zeichenfolge "Rogers Park" verknüpfen zum Ergebnis von rogersPark.length

const neighborhoodsArray = [
  {
    name: 'Rogers Park',
    number: rogersPark.length,
  },
  {
    name: 'Wicker Park',
    number: wickerPark.length,
  },
]

Und schließlich konnte ich diese Werte durchlaufen.

for (let neighborhood of neighborhoodsArray) {
  console.log(neighborhood.name, neighborhood.name)
}

for...of wird zum Iterieren durch Arrays verwendet. Hier ist die Ausgabe.

Rogers Park 1
Wicker Park 2

Das hat technisch funktioniert, aber jetzt tue ich, was ich beim ersten Mal nicht tun wollte – mich zu wiederholen. Dies stellte sich also als ineffiziente Verwendung des filter() heraus Methode, aber das ist in Ordnung. Nicht alles, was Sie in Ihre Lernreise schreiben, wird perfekt sein, und jetzt können wir es umgestalten.

Verwendung von Reduce() zum Zählen von Instanzen von Werten in einem Objekt

reduce() wird wie map() geschrieben und filter() , aber die innere Funktion benötigt zwei Parameter. Es hat auch eine zusätzliche Stelle am Ende, wo wir ein leeres Objekt einfügen {} . Dies könnte auch ein leeres Array [] sein , aber in unserem Beispiel wird es ein Objekt sein. Dieser zusätzliche Parameter ist der Anfangswert , also 0 standardmäßig.

const neighborhoodCount = data.cafes.reduce((sums, value) => {}, {})

Ich bin auf dieses praktische Snippet gestoßen, das ein Objekt erstellt, das alle unsere endgültigen Nachbarschaftszählungen enthält.

const neighborhoodCount = data.cafes.reduce((sums, cafe) => {
  sums[cafe.neighborhood] = (sums[cafe.neighborhood] || 0) + 1
  return sums
}, {})

Jetzt durchlaufen wir das Objekt, das wir mit for...in erstellt haben , das zum Durchlaufen von Objektschlüsseln verwendet wird.

for (let neighborhood in neighborhoodCount) {
  console.log(neighborhood, neighborhoodCount[neighborhood])
}

Und hier ist die Ausgabe.

Rogers Park 1
Wicker Park 2

Wir haben das gleiche Ergebnis wie unser ineffizienter Code von vorher mit nur ein paar kurzen Zeilen.

Sobald das fertig war, fügte ich es in das DOM ein, anstatt es nur auf der Konsole auszudrucken. Gerade eine neue ID zu index.html hinzugefügt .

<div id="neighborhoods"></div>

Übergeben Sie dann die reduzierten Werte an den neuen div .

// Create a sidebar
const sidebar = document.getElementById('neighborhoods')

// Print all neighborhoods in sidebar
for (let neighborhood in neighborhoodCount) {
  const p = document.createElement('p')

  p.innerHTML = `<b>${neighborhood}</b> : ${neighborhoodCount[neighborhood]}`
  sidebar.appendChild(p)
}

Ich habe etwas CSS, ein Kaffeetassenbild als Markierung, eine andere Karte und weitere Orte hinzugefügt, auf die ich nicht eingehen werde, da sie nicht im Mittelpunkt des Artikels stehen. Die Quelle für die gesamte scripts.js Datei, die wir gerade erstellt haben, ist genau hier. Unten sehen Sie, wie die endgültige Version aussieht.

Sie können die endgültige Quelle für die Karte hier anzeigen oder das abgeschlossene Projekt hier anzeigen, falls etwas unklar war.

Eine Zusammenfassung von map(), filter() und Reduce()`

Hier ist ein sehr kurzer Überblick über die Unterschiede zwischen map() , filter() und reduce() , mit einem sehr einfachen Array von [1, 2, 3, 4] .

const numbers = [1, 2, 3, 4]

map()

Verwenden Sie map() um ein Array zu erhalten, bei dem jeder Wert um eins erhöht wird.

const numbersIncremented = numbers.map((x) => {
  return x + 1
})

numbersIncremented
[ 2, 3, 4, 5]

filter()

Verwenden Sie filter() um ein Array verfügbarer Werte größer als 2 zu erhalten.

const numbersFiltered = numbers.filter((x) => {
  return x > 2
})

numbersFiltered
[ 3, 4 ]

reduzieren()

Verwenden Sie reduce() um die Summe der Zahlen im Array zu erhalten.

const numbersReduced = numbers.reduce((x, y) => {
  return x + y
})

numbersReduced
10

Fazit

Hier hast du es. Ein kleines Projekt mit einigen Problemlösungen, die map() verwenden , filter() , und reduce() nebenbei, mit prägnanten Schnipseln zur Überprüfung. Kommentare und Verbesserungen willkommen!

Zum Schluss ist hier meine musikalische Interpretation von Mad World von Tears for Fears. Fühlen Sie sich frei, diesen unverfeinerten Ivory- und Ivory-Klassiker in Ihre Musikbibliothek zu importieren.