banner
biuaxia

biuaxia

"万物皆有裂痕,那是光进来的地方。"
github
bilibili
tg_channel

Map-Reduce-Filter 模式處理集合元素

標題:Map-Reduce-Filter 模式處理集合元素
日期:2022-07-04 10:37:00
toc:false
index_img:http://api.btstu.cn/sjbz/?lx=m_dongman&cid=9
類別:

  • Go
    標籤:
  • 用戶
  • 遍歷
  • 計算
  • 統計
  • 字串
  • 函數

日常開發過程中,要處理陣列、片段、字典等集合類型,常規做法都是迴圈迭代進行處理。比如將一個字典類型用戶片段中的所有年齡屬性值提取出來,然後求和,常規實現是通過迴圈遍歷所有片段,然後從用戶字典鍵值對中提取出年齡欄位值,再依次進行累加。

針對簡單的單個場景,這麼實現沒什麼問題,但這是典型的面向過程思維,而且程式幾乎沒有什麼複用性可言:每次處理類似的問題都要編寫同樣的程式模板,比如計算其他欄位值,或者修改類型轉換邏輯,都要重新編寫實現程式。

在函數式編程中,我們可以通過 Map-Reduce 技術讓這個功能實現變得更優雅,程式複用性更好。

Map-Reduce 並不是一個整體,而是要分兩步實現:Map 和 Reduce,Map-Reduce 模型:先將字典類型片段轉化為一個字串類型片段(Map,字面意思就是一一映射),再將轉化後的片段元素轉化為整型後累加起來(Reduce,字面意思就是將多個集合元素通過迭代處理減少為一個)。

有的時候,為了讓 Map-Reduce 程式更加堅固(排除無效的欄位值),或者只對指定範圍的資料進行統計計算,還可以在 Map-Reduce 基礎上引入 Filter(過濾器),對集合元素進行過濾。

package main

import (
  "fmt"
  "strconv"
  "time"
)

func main() {
  var users = []map[string]string{
    {
      "name": "xmp",
      "age":  "11",
    },
    {
      "name": "wlm",
      "age":  "24",
    },
    {
      "name": "wjf",
      "age":  "30",
    },
    {
      "name": "lm",
      "age":  "50",
    },
  }
  fmt.Println("總年齡:", ageSum(users))

  fmt.Println("總年齡:", handlerMapReduce(users))

  fmt.Println("總年齡(過濾40以上和20歲以下):", handlerValidUsers(users))
}

func ageSum(users []map[string]string) int {
  startTime := time.Now()
  var result int
  for _, user := range users {
    age, _ := strconv.Atoi(user["age"])
    result += age
  }
  fmt.Println("\tageSum 總耗時:", time.Since(startTime))
  return result
}

func handlerValidUsers(users []map[string]string) int {
  validUsers := filterMap(users, func(item map[string]string) bool {
    ageStr, ok := item["age"]
    if !ok {
      return false
    }
    age, err := strconv.Atoi(ageStr)
    if err != nil {
      return false
    }
    if age > 40 || age < 20 {
      return false
    }
    return true
  })
  return handlerMapReduce(validUsers)
}

func filterMap(items []map[string]string, f func(map[string]string) bool) []map[string]string {
  startTime := time.Now()
  var result []map[string]string
  for _, item := range items {
    if f(item) {
      result = append(result, item)
    }
  }
  fmt.Println("\tfilterMap 總耗時:", time.Since(startTime))
  return result
}

func handlerMapReduce(users []map[string]string) int {
  startTime := time.Now()
  items := mapToString(users, func(user map[string]string) string {
    return user["age"]
  })
  result := fieldSum(items, func(str string) int {
    val, _ := strconv.Atoi(str)
    return val
  })
  fmt.Println("\thandlerMapReduce 總耗時:", time.Since(startTime))
  return result
}

func mapToString(items []map[string]string,
  f func(map[string]string) string) []string {
  newSlice := make([]string, len(items))
  for _, item := range items {
    newSlice = append(newSlice, f(item))
  }
  return newSlice
}

func fieldSum(items []string, f func(string) int) int {
  var result int
  for _, item := range items {
    result += f(item)
  }
  return result
}

不過分開呼叫 Map、Reduce、Filter 函數不太優雅,我們可以通過裝飾器模式將它們層層嵌套起來,或者通過管道模式(Pipeline)讓這個呼叫邏輯可讀性更好,更優雅。

載入中......
此文章數據所有權由區塊鏈加密技術和智能合約保障僅歸創作者所有。