golang lodash风格的高效工具库插件gofp的使用

golang lodash风格的高效工具库插件gofp的使用

gofp

一个简单的Go工具库

Go语言在数据结构如slicemap方面没有提供许多基本的内置函数。这个库提供了最常用的工具函数,灵感来自lodash(一个Javascript工具库)。

gofp的主要特性

  • 使用Pipe(), Compose(), Reduce(), Map(), Filter(), Extend(), Find()等函数更容易实现函数式编程
  • 该库提供了许多处理集合或切片相关操作的实用函数
  • 通过Get函数可以轻松访问map、slice甚至结构体中的任何属性
  • 工具函数基于interface{}实现,主要目标是尽可能不使用reflect

安装

在终端运行以下命令安装:

go get github.com/rbrahul/gofp

如何使用?

以下示例展示了如何使用gofp实现pipe操作:

package main

import (
    "fmt"
    "strings"
    "github.com/rbrahul/gofp"
)

func main() {
    user := map[string]interface{}{
        "name": "John Doe",
        "age":  30,
        "contacts": map[string]interface{}{
            "email":  "johndoe@gmail.com",
            "office": "Google Inc.",
            "fax": map[string]interface{}{
                "uk": "+44-208-1234567",
            },
        },
    }
    getContacts := func(data interface{}) interface{} {
        return data.(map[string]interface{})["contacts"]
    }

    getEmail := func(data interface{}) interface{} {
        return data.(map[string]interface{})["email"]
    }
    getUpperCaseEmail := func(data interface{}) interface{} {
        return strings.ToUpper(data.(string))
    }

    email := gofp.Pipe(
        getContacts,
        getEmail,
        getUpperCaseEmail,
    )(user)

    fmt.Println("Email is: ", email) // Output: Email is: JOHNDOE@GMAIL.COM
}

文档

集合或切片最常用的工具函数

Map()

返回一个新的切片,对每个元素执行迭代器函数。Map有2个参数,第1个是切片,第2个是迭代器函数。迭代器函数必须有2个参数,索引和当前迭代值。

    ...
    mappedItems := Map([]interface{}{1, 2, 3, 4, 5},
            func(i int, item interface{}) interface{} {
                return item.(int) * item.(int)
            })
    
    fmt.Println(mappedItems) //Output: 1, 4, 9, 16, 25
    ...

Filter()

返回一个包含过滤元素的新切片。新切片包含满足迭代器函数条件的元素。

    ...
    filteredItems := Filter([]interface{}{12, 16, 18, 20, 23, 40, 25},
        func(i int, age interface{}) bool {
            return age.(int) >= 20
        })

    fmt.Println(filteredItems) //Output:  20, 23, 40, 25
    ...

Find()

返回切片中第一个满足迭代器函数条件的元素。如果没有满足条件的元素则返回nil。

    ...
    user := Find([]interface{}{
            map[string]interface{}{"name": "Ron", "sex": "male", "age": 17},
            map[string]interface{}{"name": "Raymond", "sex": "male", "age": 20},
            map[string]interface{}{"name": "Sofia", "sex": "female", "age": 20},
            map[string]interface{}{"name": "Roni", "sex": "male", "age": 30},
        }, func(i int, person interface{}) bool {
            return person.(map[string]interface{})["age"].(int) >= 18
        })
    fmt.Println(user) //Output:  {"name": "Raymond", "sex": "male", "age": 20}
    ...

Reduce()

对切片中的每个元素执行迭代器函数,最终得到一个累积值。

    ...
    reducedItems := Reduce([]interface{}{10, 20, 30, 40},
        func(index int, current interface{}, accumulator interface{}, source []interface{}) interface{} {
            return accumulator.(int) + current.(int)
        }, 0)
    fmt.Println(reducedItems) //Output:  100
    ...

Every()

如果每个元素都匹配给定迭代器函数的条件则返回true

    ...
    isEveryOneIsAdult := Every([]interface{}{18, 20, 23, 40, 25},
        func(i int, age interface{}) bool {
            return age.(int) >= 18
        })
    fmt.Println(isEveryOneIsAdult) //Output:  true
    ...

Any()

如果有任何元素匹配给定迭代器函数的条件则返回true

    ...
    hasAnyAdult := Any([]interface{}{18, 20, 23, 40, 25},
        func(i int, age interface{}) bool {
            return age.(int) >= 18
        })
    fmt.Println(hasAnyAdult) //Output:  true
    ...

GroupBy()

返回一个新的map,键是迭代器函数的运行结果。

    ...
    groupedData := GroupBy([]interface{}{
            map[string]interface{}{"name": "Ron", "sex": "male", "age": 17},
            map[string]interface{}{"name": "Raymond", "sex": "male", "age": 20},
            map[string]interface{}{"name": "Sofia", "sex": "female", "age": 20},
            map[string]interface{}{"name": "Roni", "sex": "male", "age": 30},
        }, func(person interface{}) string {
            return strconv.Itoa(person.(map[string]interface{})["age"].(int))
        })
    fmt.Println(groupedData) 
    /*
    Output:
      { 
          "17": [{"name": "Ron", "sex": "male", "age": 17}],
          "20": [
                {"name": "Raymond", "sex": "male", "age": 20},
                {"name": "Sofia", "sex": "female", "age": 20}
               ],
          "30": [{"name": "Roni", "sex": "male", "age": 30}]
     }
    */
    ...

Chunk()

返回一个新的切片切片,每个子切片有固定数量的元素。

    ...
    chunkedItems := Chunk([]interface{}{1, 2, 3, 4, 5}, 2)
    fmt.Println(chunkedItems) //Output:  {{1,2},{3,4},{5}}
    ...

Reverse()

返回一个元素顺序反转的新切片。

    ...
    reveresed := Reverse([]interface{}{10, 20, 30, 40, 50})
    fmt.Println(reveresed) //Output:  {50,40,30,20,10}
    ...

Range()

返回一个从第1个参数到第2个参数的范围切片。

    ...
    rangeItems := Range(5, 10)
    fmt.Println(rangeItems) //Output:  {5,6,7,8,9,10}
    ...

Uniq()

返回一个去重后的新切片。

    ...
    uniqueItems := Uniq([]interface{}{1, 2, 2, 3, 10, 4, 5, 10, 100})
    fmt.Println(uniqueItems) //Output:  {1,2,3,10,4,5,100}
    ...

Head()

返回切片的第一个元素。

    ...
    firstItem := Head([]interface{}{
            map[string]interface{}{"name": "Ron", "sex": "male", "age": 17},
            map[string]interface{}{"name": "Raymond", "sex": "male", "age": 20},
            map[string]interface{}{"name": "Sofia", "sex": "female", "age": 20},
            map[string]interface{}{"name": "Roni", "sex": "male", "age": 30},
        })
    fmt.Println(firstItem) //Output:  {"name": "Ron", "sex": "male", "age": 17}
    ...

Tail()

返回切片的最后一个元素。

    ...
    lastItem := Tail([]interface{}{
            map[string]interface{}{"name": "Ron", "sex": "male", "age": 17},
            map[string]interface{}{"name": "Raymond", "sex": "male", "age": 20},
            map[string]interface{}{"name": "Sofia", "sex": "female", "age": 20},
            map[string]interface{}{"name": "Roni", "sex": "male", "age": 30},
        })
    fmt.Println(lastItem) //Output:  {"name": "Roni", "sex": "male", "age": 30}
    ...

Fill()

返回一个新切片,从开始到结束索引用给定字符串替换所有元素。

    ...
    filledItems := Fill([]interface{}{1, 2, 3, 4, 5, 6, 7}, "*", 1, 5)
    fmt.Println(filledItems) //Output:  {1, *, *, *, *, 6, 7}
    ...

IndexOf()

返回切片中第一次出现给定元素的索引。

    ...
    index := IndexOf([]interface{}{1, 2, 2, 3, 10, 4, 5, 10, 100}, 10)
    fmt.Println(index) //Output: 4
    ...

Contains()

如果给定元素存在于切片中则返回true

    ...
    exists := Contains([]interface{}{1, 2, 2, 3, 10, 4, 5, 10, 100}, 10)
    fmt.Println(exists) //Output: true
    ...

ChooseRandom()

随机返回切片中的一个元素。

    ...
    randomElement := ChooseRandom([]interface{}{1, 2, 3, 4, 5, 10, 100})
    fmt.Println("Could be any:",randomElement) //Output Could be any: 4 
    ...

Shuffle()

返回一个元素随机排序的新切片。

    ...
    shuffledItems := ChooseRandom([]interface{}{1, 2, 3, 4, 5, 10, 100})
    fmt.Println(shuffledItems) //Output: {100, 2, 1, 4, 5, 3, 10} 
    ...

Map相关的工具函数

Keys()

返回map的键切片。

    ...
    keys := Keys(map[string]interface{}{
        "firstName": "John", 
        "lastName": "Doe",
        "age": 32
        })
    fmt.Println(keys) //Output: {firstName, lastName, age}
    ...

Values()

返回map的值切片。

    ...
    values := Values(map[string]interface{}{
        "firstName": "John", 
        "lastName": "Doe",
        "age": 32
        })
    fmt.Println(values) //Output: {John, Doe, 32}
    ...

Has()

如果键存在于map中则返回true

    ...
    exists := Has(map[string]interface{}{
        "firstName": "John", 
        "lastName": "Doe",
        "age": 32
        }, "age")
    fmt.Println(exists) //Output: true
    ...

Pick()

返回一个新map,只包含指定的属性。

    ...
    pickedData := Pick(map[string]interface{}{
        "firstName": "John", 
        "lastName": "Doe",
        "age": 32
        }, []string{"lastName"})
    fmt.Println(pickedData) //Output: {"lastName": "Doe"}
    ...

Omit()

返回一个新map,省略给定的键。

    ...
    omittedData := Omit(map[string]interface{}{
        "firstName": "John", 
        "lastName": "Doe",
        "age": 32
        }, []string{"lastName"})
    fmt.Println(omittedData) //Output: {"firstName": "John", "age": 32}
    ...

MapValues()

返回一个新map,对每个值应用迭代器函数。

    ...
    mappedValues := MapValues(map[string]interface{}{
        "firstName": "john",
        "lastName": "doe",
        "gender": "unknown"}, func(value interface{}) interface{} {
        return strings.Title(value.(string))
    })
    fmt.Println(mappedValues) //Output: {"firstName": "JOHN",  "lastName":"DOE": 32, "gender":"UNKNOWN"}
    ...

MapKeys()

返回一个新map,对每个键应用迭代器函数。

    ...
    mappedKeys := MapKeys(map[string]interface{}{
        "firstName": "john",
        "lastName": "doe",
        "gender": "unknown"}, func(value interface{}) interface{} {
        return strings.Title(value.(string))
    })
    fmt.Println(mappedKeys) //Output: {"FIRSTNAME": "john","LASTNAME": "doe","GENDER": "unknown"}
    ...

Get()

返回给定路径的值。如果没有数据则返回nil

    ...
    data := map[string]interface{}{
        "age":  30,
        "male": true,
        "contacts": map[string]interface{}{
            "office": 12345,
            "fax": map[string]interface{}{
                "uk": "+44-208-1234567",
            },
            "address": map[string]interface{}{
                "post_code":    "SW3",
                "street":       "10 Downing Street",
                "geo_location": []string{"51.529011463529636", "-0.1098365614770662"},
            },
        },
    }
    geoLocationFromGet := Get(data, "contacts.address.geo_location.0")
    fmt.Println(geoLocationFromGet) //Output: 51.529011463529636
    ...

Extend()

返回一个新map,用给定map扩展值。

    ...
    extendedMap := Extends(
        map[string]interface{}{
        "firstName": "john",
        "lastName": "doe",
        "gender": "unknown"
        },
         map[string]interface{}{
        "gender": "male"
        })
    fmt.Println(extendedMap) //Output: {"firstName": "john","lastName": "doe","gender": "male"}
    ...

更多关于golang lodash风格的高效工具库插件gofp的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html

1 回复

更多关于golang lodash风格的高效工具库插件gofp的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


gofp - Golang 的 Lodash 风格工具库

gofp 是一个受 JavaScript 的 Lodash 库启发的 Golang 实用工具库,提供了许多函数式编程风格的实用函数,可以大大简化日常开发中的集合操作、数据处理等任务。

安装

go get github.com/rbrahul/gofp

主要功能与示例

1. 集合操作

package main

import (
	"fmt"
	"github.com/rbrahul/gofp"
)

func main() {
	// 过滤
	numbers := []int{1, 2, 3, 4, 5, 6}
	even := gofp.Filter(numbers, func(n int) bool {
		return n%2 == 0
	})
	fmt.Println(even) // [2 4 6]

	// 映射
	doubled := gofp.Map(numbers, func(n int) int {
		return n * 2
	})
	fmt.Println(doubled) // [2 4 6 8 10 12]

	// 归约
	sum := gofp.Reduce(numbers, func(acc, curr int) int {
		return acc + curr
	}, 0)
	fmt.Println(sum) // 21
}

2. 查找操作

// 查找第一个匹配项
found := gofp.Find(numbers, func(n int) bool {
	return n > 3
})
fmt.Println(found) // 4

// 检查是否存在匹配项
exists := gofp.Contains(numbers, 5)
fmt.Println(exists) // true

// 查找索引
index := gofp.FindIndex(numbers, func(n int) bool {
	return n == 3
})
fmt.Println(index) // 2

3. 切片操作

// 分块
chunks := gofp.Chunk(numbers, 2)
fmt.Println(chunks) // [[1 2] [3 4] [5 6]]

// 去重
duplicates := []int{1, 2, 2, 3, 4, 4, 5}
unique := gofp.Uniq(duplicates)
fmt.Println(unique) // [1 2 3 4 5]

// 差集
a := []int{1, 2, 3, 4}
b := []int{2, 4, 5}
diff := gofp.Difference(a, b)
fmt.Println(diff) // [1 3]

4. 实用函数

// 随机打乱
shuffled := gofp.Shuffle(numbers)
fmt.Println(shuffled) // 随机顺序

// 分组
type Person struct {
	Name string
	Age  int
}
people := []Person{
	{"Alice", 25},
	{"Bob", 30},
	{"Charlie", 25},
}
grouped := gofp.GroupBy(people, func(p Person) int {
	return p.Age
})
fmt.Println(grouped) // map[25:[{Alice 25} {Charlie 25}] 30:[{Bob 30}]]

// 键值对转换
keyBy := gofp.KeyBy(people, func(p Person) string {
	return p.Name
})
fmt.Println(keyBy) // map[Alice:{Alice 25} Bob:{Bob 30} Charlie:{Charlie 25}]

5. 链式调用

result := gofp.Chain(numbers).
	Filter(func(n int) bool { return n > 2 }).
	Map(func(n int) int { return n * 3 }).
	Value()
fmt.Println(result) // [9 12 15 18]

性能考虑

gofp 在设计时考虑了性能因素:

  1. 大多数操作都是 O(n) 时间复杂度
  2. 避免不必要的内存分配
  3. 提供直接的函数调用而非复杂的对象模型

与原生 Go 代码对比

虽然 Go 的标准库已经提供了一些基础功能,但 gofp 提供了更简洁的 API:

// 原生 Go 过滤
var filtered []int
for _, n := range numbers {
	if n%2 == 0 {
		filtered = append(filtered, n)
	}
}

// gofp 方式
filtered := gofp.Filter(numbers, func(n int) bool { return n%2 == 0 })

适用场景

  1. 数据处理和转换
  2. API 响应处理
  3. 集合操作密集的业务逻辑
  4. 需要函数式编程风格的代码

注意事项

  1. 对于极高性能敏感的场景,直接循环可能更高效
  2. 某些复杂操作可能不如专门优化的代码高效
  3. 类型安全需要开发者自己保证

gofp 为 Golang 开发者提供了一种更简洁、更声明式的编程方式,特别适合那些熟悉 Lodash 风格 API 的开发者。

回到顶部