范型下,优雅的 Lodash 风

本文介绍了Go语言在引入范型后,如何利用samber/lo库实现优雅的数据结构转换,如slice到map、map到slice。该库提供了丰富的转换函数,使得代码更加简洁和高效,适合后端开发人员使用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

推荐理由

go 语言比较崇尚简单,所以在内嵌包中没有提供过多帮助性的函数,尤其在范型出来前想要写一个简单的对 slice 和 map 间互相转化的方法可能就需要很多行代码,并且这样并不是很优雅。如今,go 范型已经得到官方的正式发布,samber/lo 又提供了优雅的 lodash 风格的工具函数,正是代码重构的好时机。

常见的使用场景

这里只是用 slice 和 map 两个常见结构举个栗子,lo 中支持超多的转化帮助函数供开发使用。

slice → map

在批量接口中,request 中携带 idList ,返回 map response​​​​​​

struct Request {
	IDList []int64
}

struct Response {
	Metas map[int64]Meta
}

// 用于模拟数据库查询数据
struct Meta {
	ID int64
	Name string
}

func MGetMeta(req Request) Response{
	result := make([]Meta,0, len(req.IDList))
	// 执行 db query
	db.Raw("select * from meta where id in ?", req.IDList).Scan(&result)
	return Response{
		Metas: MetaResult2Map(result),
	}
}

// 旧代码: 针对不同的类型需要写不同的转化函数
func MetaResult2Map(metas []Meta)map[int64]Meta{
	res := make(map[int64]Meta)
	for _, m := range metas{
		res[m.ID] = m
	}
	return res
}

// 采用 lo:代码清晰,支持多种结构
func MGetMeta(req Request) Response{
	result := make([]Meta,0, len(req.IDList))
	// 执行 db query
	db.Raw("select * from meta where id in ?", req.IDList).Scan(&result)
	return Response{
		Metas: lo.KeyBy[int64,Meta](a, func(m Meta)int64{
			return m.ID
		}),
	}
}

map → slice

// 在对批量接口接口去重后,可能得到这样类型参数 map[int64]bool
// 然后在db查询时其实只用map 的 key 的 slice
// 旧代码实现
func Int64MapKeys(m map[int64]interface{}) []int64{
	res := make([]int64,0,len(m))
	for k := range m{
		res = append(res, k)
	}
	return res
}

// 使用 lo
lo.Keys[int64,bool](map[int64]bool{1: true,2:true,3:true})

// 批量的聚合接口
// 比如 定时任务中,扫表获取了一堆数据,分属于不同的业务方需要分别调用
// 此时参数转化为 []T -> map[K][]T ,安装 T 中的某个字段进行分组
func Group(m []T)map[K][]T{
	res := make(map[K][]T)
	for _, v := range m{
		if _, ok := res[v.Key];ok {
			res[v.Key] = append(res[v.Key],v)
		}else{
			res[v.Key] = []T{v}
		}
	}
	return res
}

// 使用 lo
lo.GroupBy([]Meta{{ID: 1,Name: "1"},{ID: 2, Name: "2"},{ID: 1,Name: "3"}}, func(t Meta) int64 {
		return t.ID
})

总结

samber/lo 使用的开源 MIT 协议中提供了很多转化数据结构的方法,让日常开发更加方便优雅,而且其中的代码并不复杂,全部采用 go1.18 范型标注,对于初学者而言是个学习范型的好地方。笔者就是一边自己实现,一边对照 samber/lo 实现学习完这个库的。

参考

文档:

https://pkg.go.dev/github.com/samber/lo#KeyBy

github:

https://github.com/samber/lo

### JavaScript 分割数组为多个子数组的方法 在 JavaScript 中,分割数组为多个子数组是一种常见的操作需求。以下是几种实现这一目标的方式。 #### 1. 使用 `Array.prototype.slice` 手动分片 这是最基础也是最常见的方法之一。通过循环遍历原数组并使用 `slice` 函数按固定大小切割出子数组[^3]。 ```javascript function chunkArray(array, size) { let result = []; for (let i = 0; i < array.length; i += size) { result.push(array.slice(i, i + size)); } return result; } const originalArray = [1, 2, 3, 4, 5]; console.log(chunkArray(originalArray, 2)); // 输出 [[1, 2], [3, 4], [5]] ``` 这种方法简单明了,适用于大多数情况下的数组切分任务。 --- #### 2. 利用递归方式进行分割 如果偏好函数式编程风格或者想尝试不同的算法思维,可以采用递归来解决这个问题[^4]。 ```javascript function recursiveChunk(array, size) { if (array.length === 0) return []; return [array.slice(0, size), ...recursiveChunk(array.slice(size), size)]; } const anotherExample = [6, 7, 8, 9, 10]; console.log(recursiveChunk(anotherExample, 3)); // 输出 [[6, 7, 8], [9, 10]] ``` 尽管这种做法可能不如迭代版本直观,但它确实提供了一种优雅的选择,并且有助于理解递归的工作原理。 --- #### 3. 结合高阶函数简化流程 现代 JavaScript 鼓励开发者充分利用其内置的高阶函数特性来编写更加简洁高效的代码。比如我们可以结合 `reduce` 实现同样的效果[^5]。 ```javascript function reduceChunk(array, size) { return array.reduce((accumulator, currentElement, index) => { const lastSubArray = accumulator[accumulator.length - 1]; if (!lastSubArray || lastSubArray.length === size) { accumulator.push([currentElement]); } else { lastSubArray.push(currentElement); } return accumulator; }, []); } const yetAnotherTestSet = ["A", "B", "C", "D", "E"]; console.log(reduceChunk(yetAnotherTestSet, 2)); // 输出 [["A", "B"], ["C", "D"], ["E"]] ``` 这段代码利用了累加器的概念逐步构建最终的结果列表,同时保持良好的可读性和维护性。 --- #### 4. 第三方库的支持 除了纯手写逻辑外,还可以借助一些成熟的第三方类库(如 Lodash)快速达成目的。Lodash 提供了一个专门用于此项工作的工具——_.chunk() 方法[^6]。 ```javascript const _ = require('lodash'); const sampleData = [true, false, null, undefined, NaN]; console.log(_.chunk(sampleData, 3)); // 输出 [[true, false, null], [undefined, NaN]] ``` 虽然引入额外依赖可能会增加项目复杂度,但在某些特定场合下它们无疑能显著提升开发效率。 --- ### 总结 综上所述,无论你是倾向于传统控制流还是现代化声明式编码范型,在 JavaScript 生态体系内都有多种途径可供选择去完成将大数组拆解成若干小子数组的任务。具体选用哪一种取决于实际应用场景和个人喜好等因素考量。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值