JavaScript >> Javascript 文檔 >  >> Tags >> map

JavaScript 中的 Map、Filter 和 Reduce 示例

我不了解你,但仔細閱讀 foo 的無數示例對我沒有多大幫助 和 bar 試圖弄清楚如何使用編程語言的特性。有一些 JavaScript/ES6 概念很難作為獨立的代碼片段來理解,而不了解它們是如何作為一個更大的項目的一部分工作的,所以我將在本文中介紹我如何使用它們的示例。

最近,我一直在探索芝加哥並參觀當地的各種咖啡店,以一種溫和、愉快的氛圍寫作,就像 J.K.羅琳。在我不小心參觀了一個我不太喜歡但忘記了的地方之後,我想這可能是一個有趣的小項目,可以將我寫的所有咖啡館都繪製在一張網絡地圖上。

還有一次,我在上一份工作中使用 Google Maps API 為餐廳概念繪製了多個商店位置。對於這個項目,我決定使用 Leaflet,一個用於交互式地圖的 JavaScript 庫。

這是我創建的最終地圖項目:Cafétography

對於本文,您可以為您自己的社區或您想跟踪的世界創建自己的 web 地圖,或者您可以簡單地查看我的 map() 示例 , filter()reduce() .

先決條件

  • HTML、CSS 和 JavaScript 的基本知識。回顧一下 JavaScript 變量和數據類型,您將掌握到目前為止所需的大部分 JavaScript 知識。

目標

  • 下一個目標是使用 reduce()map() 在現實世界的示例(地圖)中簡化並使我們的代碼更高效和 DRY (D 不是R 重複 Y 我們自己 )。我們將使用 reduce() 獲取每個芝加哥社區的咖啡店數量,我們將使用 map() 從外部 JSON 提要中提取數據並在網絡地圖上顯示位置。

讓我們解決一些問題。

設置網絡地圖

這是您可以使用的 CodePen 上的示例項目。它只需要一個 html 文件 (index.html ) 加載了必備的 CSS 和 JavaScript 傳單文件:

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

一個html div id 為 map

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

一些基本的 CSS (style.css ) 來顯示地圖:

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

而這個 JavaScript (scripts.js ) 加載地圖和地圖數據。

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)

一切就緒後,加載到 Leaflet 網絡地圖中的外觀如下。

同樣,您可以在 CodePen 上找到並派生整個事情的工作版本,但您必須將其傳輸到您自己的服務器或在本地工作。

使用 JSON 添加位置標記

現在我想開始將位置作為標記添加到地圖上。我可以通過使用緯度和經度製作一堆變量並使用 addTo() 輕鬆做到這一點 方法。假設我想將 El Meson、Wormhole 和 Ipsento 添加到我的地圖中。我將它保持在三個以保持簡短和簡單。下面是我將如何開始。

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)

這似乎還不錯——我只需要為每個新標記添加一個新條目。但是,一旦我開始在彈出窗口中添加每個位置的更多信息,它就會變得更加冗長。

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...

這是加載了標記和彈出窗口後的樣子。

現在,為我遇到的每一家咖啡店添加一個新的咖啡店開始變得很痛苦。創建一個 JSON 文件並循環它會容易得多。如果您以前從未使用過 JSON,我強烈建議您閱讀此 JSON 教程,從頭開始學習並進行一些練習。

此時,我將刪除 scripts.js 中的所有標記信息 並使用一個對象製作一個 JSON 文件,該對象包含一個包含我所有咖啡館位置對象的數組。一開始跟踪所有方括號和大括號可能有點棘手。

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."
    }
  ]
}

好的。現在,我們將每個位置的所有信息(名稱、緯度、經度、鄰域和其他詳細信息)整齊地放入 JSON 文件中。現在,我們如何將那個 JSON 文件放到頁面上?

我們不會使用 jQuery——只使用普通的 JavaScript——所以這是一個稍微複雜的過程。我將參考如何在 PHP 或 JavaScript 中使用 JSON 數據以獲得對代碼的進一步解釋,但這裡是我們如何打開和訪問我們的 map.json 文件。

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()

上面寫著 Begin accessing JSON data here 我們將從 map.json 開始處理數據 ,已放在data中 多變的。確保 map.json 不是本地文件 URL。請參閱以下信息。

使用 map() 循環遍歷 JSON 數據

以前我們為每個標記創建一個新變量,並將所有信息手動放入該變量中。現在我們將從 JSON 中提取所有 idata,我們將使用 map() .

map() 接受一個數組並使用原始數組中每個元素的函數結果創建一個新數組。例如,您可以創建一個 [1, 2, 3] 數組 並通過 map() 應用函數 將數組中的每個數字加一。你最終會得到 [2, 3, 4] .

獲取 cafes 內的數據 在 JSON 中,我們將使用 map() data.cafes 上的方法 裡面有一個函數。

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

我只是打算用 ES6 箭頭函數重寫那個函數,讓它更簡潔。

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

現在為了訪問 JSON 提要中的任何屬性,我們將在 cafe 上使用點符號 目的。所以 cafe.name 的第一次迭代 將返回 El Meson ,第二個Wormhole , 等等。我在這裡所做的只是採用相同的 L.Marker 之前的函數,並將所有靜態值替換為動態屬性。

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)
})

這個例子使用的是模板文字字符串,它使用反引號並且可以跨越多行以及包含帶有轉義而不是串聯的變量。如果您不熟悉模板文字,請查看此內容。

這是完整的 scripts.js 歸檔到現在。

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()

當然,map() 比我們在這個例子中所做的要多得多,但這是一個很好且實用的開始。

使用 filter() 查找匹配值

在這一點上,我已經創建了一個包含所有標記的地圖。在我們的簡化示例中,有 3 家咖啡館,但我最終選​​擇了大約 12 家。每個咖啡館都在一個街區,我有幾個街區有不止一家咖啡館我去過。我想數一數我在每個街區去過多少家咖啡館。

在我們的示例中,三個咖啡館有兩個社區 - 一個在 Rogers Park,兩個在 Wicker Park。以這樣一個小例子,當然很容易寫成“羅傑斯公園:1;柳條公園:2”,但時間越長,社區和咖啡館越多,就越需要自動化這個過程。

我們在 JSON 文件中有我們需要的所有數據,但我不確定如何簡潔地計算所有實例。一開始想用filter() 找出在整個提要中找到了每個社區名稱的實例數。 filter() 寫法類似於 map() 它還創建了一個新數組,但它的函數執行測試並過濾掉所有未通過的內容。 filter() 遵循與 map() 相同的語法 .

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

下面,我對每個社區進行測試,將過濾掉所有與我的測試不匹配的結果。

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

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

使用這種方法,我必須創建一個連接字符串 "Rogers Park" 的對像數組 rogersPark.length 的結果

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

最後我可以遍歷這些值。

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

for...of 用於遍歷數組。這是輸出。

Rogers Park 1
Wicker Park 2

這在技術上是可行的,但現在我正在做我第一次不想做的事情——重複自己。所以這被證明是對 filter() 的低效使用 方法,但沒關係。並非您在學習過程中編寫的所有內容都是完美的,現在我們可以對其進行重構。

使用 reduce() 計算對像中值的實例

reduce() 寫成 map()filter() ,但內部函數將採用兩個參數。它的末尾還有一個額外的位置,我們將在其中插入一個空對象 {} .這也可以是一個空數組 [] ,但在我們的示例中,它將是一個對象。這個額外的參數是初始值 ,即 0 默認情況下。

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

我遇到了這個方便的片段,它將創建一個包含我們所有最終鄰域計數的對象。

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

現在我們將遍歷我們用 for...in 創建的對象 ,用於遍歷對象鍵。

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

這是輸出。

Rogers Park 1
Wicker Park 2

我們得到了與之前效率低下的代碼相同的結果,只需幾行代碼。

完成後,我將其插入到 DOM 中,而不是僅僅將其打印到控制台。剛剛向 index.html 添加了一個新 id .

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

然後將減少的值傳遞給新的 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)
}

我添加了一些 CSS、一個咖啡杯圖像作為標記、一張不同的地圖和更多位置,因為它們不是本文的重點,所以我不會贅述。整個 scripts.js 的來源 我們剛剛創建的文件就在這裡。下面是最終版本的樣子。

您可以在此處查看地圖的最終來源,如果有任何不清楚的地方,您可以在此處查看已完成的項目。

map()、filter()和reduce()的總結`

以下是 map() 之間差異的非常簡要概述 , filter() , 和 reduce() ,使用一個非常簡單的 [1, 2, 3, 4] 數組 .

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

map()

使用 map() 得到一個數組,每個值加一。

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

numbersIncremented
[ 2, 3, 4, 5]

filter()

使用 filter() 獲取大於 2 的可用值數組。

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

numbersFiltered
[ 3, 4 ]

reduce()

使用 reduce() 得到數組中數字的總和。

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

numbersReduced
10

結論

你有它。一個使用 map() 解決問題的小項目 , filter() , 和 reduce() 一路上,有簡潔的片段供審查。感謝評論和改進!

最後,這是我為恐懼而哭泣的瘋狂世界的音樂演繹。隨意將這個未經提煉的 Ivory 和 Ivory 經典導入您的音樂庫。


Tutorial JavaScript 教程
  1. React – useState 屬性未更新

  2. 當 npm 告訴你你被水洗了

  3. Epic React 基礎知識

  4. 使用 Node.js 在 5 分鐘內開發無服務器應用程序

  5. useTranslator - 一個 React 翻譯鉤子

  6. 執行存儲為字符串的 JavaScript 代碼

  7. 如何在 2019 年贏得 javascript

  1. 使用 Typescript 和類創建 NuxtJs 項目

  2. 如何在 Heroku 上部署 Sails.js 應用程序並延長壽命

  3. javascript中的字謎查找器

  4. 為初學者學習 HTML5 和 CSS3

  5. 如何在 observable 中按順序運行訂閱

  6. 使用 typescript 響應上下文 API 狀態管理

  7. 在 Github 上合併

  1. 宣布 NSolid 版本 3.8.0

  2. Google Summer of Code 數據報廢

  3. 面向前端開發人員的 Docker

  4. 全棧?為什麼不 (Django + Vue-js) - 第 2 集