diff --git a/datetime.go b/datetime.go index d0fe9e1..5740710 100644 --- a/datetime.go +++ b/datetime.go @@ -118,24 +118,82 @@ func From(time time.Time) DateTime { return d } +// 通用的 SetDateTime 函数 +func (dt DateTime) SetDateTime(year, month, day, hour, minute, second, nanosecond int) DateTime { + newTime := time.Date(year, time.Month(month), day, hour, minute, second, nanosecond, time.UTC) + return DateTime{ + time: newTime, + DateFormat: dt.DateFormat, + TimeFormat: dt.TimeFormat, + Year: newTime.Year(), + Month: int(newTime.Month()), + Day: newTime.Day(), + Hour: newTime.Hour(), + Minute: newTime.Minute(), + Second: newTime.Second(), + Nanosecond: newTime.Nanosecond(), + } +} + func (dt DateTime) SetYear(year int, month int, day int, hour int, minute int, second int, nanosecond int) DateTime { - d := DateTime{ - time: time.Date(year, time.Month(month), day, hour, minute, second, nanosecond, time.UTC), - DateFormat: dt.DateFormat, - TimeFormat: dt.TimeFormat, - monthFormat: dt.monthFormat, - weekFormat: dt.weekFormat, - } - d.Year = d.time.Year() - d.Month = int(d.time.Month()) - d.Day = d.time.Day() - d.Hour = d.time.Hour() - d.Minute = d.time.Minute() - d.Second = d.time.Second() - d.Nanosecond = d.time.Nanosecond() - return d + return dt.SetDateTime(year, month, day, hour, minute, second, nanosecond) +} + +func (dt DateTime) Set(opts ...DataOption[DateTime]) DateTime { + for _, opt := range opts { + // 应用每个选项函数,修改 dt + opt(&dt) + } + return dt +} + +func SetDateTimeOption(setter func(dt DateTime, year, month, day, hour, minute, second, nanosecond int) DateTime) DataOption[DateTime] { + return func(dt *DateTime) { + *dt = setter(*dt, dt.Year, dt.Month, dt.Day, dt.Hour, dt.Minute, dt.Second, dt.Nanosecond) + } +} + +func WithDateTimeYear(year int) DataOption[DateTime] { + return SetDateTimeOption(func(dt DateTime, _, month, day, hour, minute, second, nanosecond int) DateTime { + return dt.SetDateTime(year, month, day, hour, minute, second, nanosecond) + }) +} + +func WithDateTimeMonth(month int) DataOption[DateTime] { + return SetDateTimeOption(func(dt DateTime, year, _, day, hour, minute, second, nanosecond int) DateTime { + return dt.SetDateTime(year, month, day, hour, minute, second, nanosecond) + }) } +func WithDateTimeDay(day int) DataOption[DateTime] { + return SetDateTimeOption(func(dt DateTime, year, month, _, hour, minute, second, nanosecond int) DateTime { + return dt.SetDateTime(year, month, day, hour, minute, second, nanosecond) + }) +} + +func WithDateTimeHour(hour int) DataOption[DateTime] { + return SetDateTimeOption(func(dt DateTime, year, month, day, _, minute, second, nanosecond int) DateTime { + return dt.SetDateTime(year, month, day, hour, minute, second, nanosecond) + }) +} + +func WithDateTimeMinute(minute int) DataOption[DateTime] { + return SetDateTimeOption(func(dt DateTime, year, month, day, hour, _, second, nanosecond int) DateTime { + return dt.SetDateTime(year, month, day, hour, minute, second, nanosecond) + }) +} + +func WithDateTimeSecond(second int) DataOption[DateTime] { + return SetDateTimeOption(func(dt DateTime, year, month, day, hour, minute, _, nanosecond int) DateTime { + return dt.SetDateTime(year, month, day, hour, minute, second, nanosecond) + }) +} + +func WithDateTimeNanosecond(nanosecond int) DataOption[DateTime] { + return SetDateTimeOption(func(dt DateTime, year, month, day, hour, minute, second, _ int) DateTime { + return dt.SetDateTime(year, month, day, hour, minute, second, nanosecond) + }) +} func (dt DateTime) SetMonth(month int, day int, hour int, minute int, second int, nanosecond int) DateTime { return dt.SetYear(dt.Year, month, day, hour, minute, second, nanosecond) } diff --git a/mapping.go b/mapping.go index 9995b41..d4faba0 100644 --- a/mapping.go +++ b/mapping.go @@ -1,47 +1,59 @@ package utils -import "reflect" +import ( + "reflect" + "strings" + "unicode" +) + +// KeyFunc defines a function type for generating map keys from struct fields +type KeyFunc func(field reflect.StructField) string // omit specify field in target struct // -// type People struct { -// Name string -// Age int -// Address string -// } -// p := &People{ -// Name: "william", -// Age: 20, -// Address: "BC", -// } -// Omit(p,[]string{"Name"}) +// type People struct { +// Name string +// Age int +// Address string +// } +// +// p := &People{ +// Name: "william", +// Age: 20, +// Address: "BC", +// } // -func Omit[T any](target T, fields []string) map[string]any { +// Omit(p,"Name") +func Omit[T any](target T, fields ...string) map[string]any { mapping := map[string]bool{} for _, value := range fields { + mapping[SnakeCase(value)] = true + mapping[value] = true mapping[value] = true } result := StructFilter(target, func(field string, val reflect.Value) bool { _, ok := mapping[field] return !ok - }) + }, nil) return result } + // pick specify field in target struct // -// type People struct { -// Name string -// Age int -// Address string -// } -// p := &People{ -// Name: "william", -// Age: 20, -// Address: "BC", -// } -// Pick(p,[]string{"Name"}) +// type People struct { +// Name string +// Age int +// Address string +// } +// +// p := &People{ +// Name: "william", +// Age: 20, +// Address: "BC", +// } // -func Pick[T any](target T, fields []string) map[string]any { +// Pick(p,[]string{"Name"}) +func Pick[T any](target T, fields ...string) map[string]any { mapping := map[string]bool{} for _, value := range fields { mapping[value] = true @@ -49,23 +61,142 @@ func Pick[T any](target T, fields []string) map[string]any { result := StructFilter(target, func(field string, val reflect.Value) bool { _, ok := mapping[field] return ok - }) + }, nil) return result } -func StructFilter[T any](target T, callback func(field string, val reflect.Value) bool) map[string]any { +func jsonKeyFunc(field reflect.StructField) string { + if jsonTag := field.Tag.Get("json"); jsonTag != "" && jsonTag != "-" { + return jsonTag + } + return "" +} + +// SnakeCase 将 CamelCase 转换为 snake_case,并处理缩写词 +func SnakeCase(s string) string { + var result []rune + n := len(s) + allUpper := true + + for _, r := range s { + if unicode.IsLower(r) { + allUpper = false + break + } + } + + if allUpper { + return strings.ToLower(s) + } + + for i := 0; i < n; i++ { + r := rune(s[i]) + + if unicode.IsUpper(r) { + if i > 0 && !unicode.IsUpper(rune(s[i-1])) { + result = append(result, '_') + } + result = append(result, unicode.ToLower(r)) + } else { + result = append(result, r) + } + } + return string(result) +} + +// GetFieldName 根据字段的 JSON 标签或字段名返回正确的字段名称 +func GetFieldName(field reflect.StructField) string { + jsonTag := field.Tag.Get("json") + if jsonTag == "" { + return SnakeCase(field.Name) + } + jsonName := strings.Split(jsonTag, ",")[0] // 获取 JSON 标签的第一个部分 + if jsonName != "" && jsonName != "-" { + return jsonName + } + return SnakeCase(field.Name) +} + +func structFilterRecursive( + value reflect.Value, + t reflect.Type, + callback func(field string, val reflect.Value) bool, + keyFunc KeyFunc, + result map[string]any, +) { + for i := 0; i < value.NumField(); i++ { + field := t.Field(i) + fieldValue := value.Field(i) + + key := keyFunc(field) + + if field.Anonymous { + // If the field is an embedded struct, recurse into it + structFilterRecursive(fieldValue, fieldValue.Type(), callback, keyFunc, result) + } else if callback(key, fieldValue) && key != "" { + result[key] = fieldValue.Interface() + } + } +} + +func StructFilter[T any]( + target T, + callback func(field string, val reflect.Value) bool, + key KeyFunc, +) map[string]any { reflectValue := reflect.ValueOf(target) reflectType := reflect.TypeOf(target) if reflectValue.Kind() == reflect.Pointer { reflectValue = reflectValue.Elem() - reflectType = reflectType.Elem() + reflectType = reflectType.Elem() + } + GenKey := key + if key == nil { + GenKey = GetFieldName } result := make(map[string]any) + structFilterRecursive(reflectValue, reflectType, callback, GenKey, result) + return result +} + +func resolvePointer(value reflect.Value) reflect.Value { + if value.Kind() == reflect.Pointer { + return reflect.Indirect(value) + } + return value +} + +func GetFields[T any](target T, callback func(reflect.Value)) { + reflectValue := reflect.ValueOf(target) + reflectValue = resolvePointer(reflectValue) for i := 0; i < reflectValue.NumField(); i++ { - field := reflectType.Field(i) - if callback(field.Name, reflectValue.FieldByName(field.Name)) { - result[field.Name] = reflectValue.FieldByName(field.Name).Interface() + field := reflectValue.Field(i) + fieldType := reflectValue.Type().Field(i) + if fieldType.Anonymous { + embeddedValue := resolvePointer(field) + if embeddedValue.Kind() == reflect.Struct { + field := embeddedValue.FieldByName(field.Type().Name()) + callback(field) + } + } else { + callback(field) } } +} + +// 泛型函数 ArrayFilter,接收一个目标数组 target 和回调函数 cb +func ArrayFilter[T any](target []T, cb func(T) []map[string]any) []map[string]any { + var result []map[string]any // 存储过滤后的结果 + + for _, item := range target { + // 调用回调函数 cb,对每个元素进行处理 + filtered := cb(item) + + // 如果 cb 函数返回非空的结果,则将其添加到最终结果中 + if len(filtered) > 0 { + result = append(result, filtered...) + } + } + return result } diff --git a/set.go b/set.go index 93535fa..bfd1dac 100644 --- a/set.go +++ b/set.go @@ -4,7 +4,7 @@ type Set[T comparable] map[T]bool func (s Set[T]) Values() []T { values := make([]T, 0) - for k, _ := range s { + for k := range s { values = append(values, k) } return values @@ -20,6 +20,17 @@ func (s Set[T]) Add(value T) bool { return s[value] } +func (s Set[T]) Adds(values ...T) bool { + allAdded := true + + for _, value := range values { + if !s.Add(value) { + allAdded = allAdded && false + } + } + return allAdded +} + func NewSet[T comparable](value ...T) Set[T] { set := make(Set[T],0) for _,val := range value { diff --git a/utils_test.go b/utils_test.go index 4e7b035..89edfba 100644 --- a/utils_test.go +++ b/utils_test.go @@ -613,11 +613,11 @@ func TestBigNumber(t *testing.T){ num15 := utils.NewBigNumber("2") result := num14.Divide(num15) if result.String() != "0.500000" { - t.Error(fmt.Sprintf(" 1 / 2 should be 0.5, but got %s",result.String())) + t.Error(fmt.Sprintf(" 1 / 2 should be 0.5, but got %s\n",result.String())) } nums16 := utils.NewBigNumber("-12") if nums16.AbsoluteValue().String() != "12" { - t.Error(fmt.Sprintf("the -12 absolute value should be 12, but got %s",nums16.AbsoluteValue().String())) + t.Error(fmt.Sprintf("the -12 absolute value should be 12, but got %s\n",nums16.AbsoluteValue().String())) } num17 := utils.NewBigNumber("12") @@ -628,5 +628,17 @@ func TestBigNumber(t *testing.T){ func TestRevese(t *testing.T){ d := utils.Reverse([]string{"A","B","C","D","E","F"}) - fmt.Printf("%v",d) + fmt.Printf("%v\n",d) +} + +func TestDatatimeOption(t *testing.T){ + dt := utils.NewDateTime() + dt = dt.Set( + utils.WithDateTimeYear(2018), + ) + if dt.Year != 2018 { + t.Error(fmt.Sprintf(" expected 2018, but got %d",dt.Year)) + } + fmt.Printf("datetime is %s\n",dt.String()) + }