# #go
Вопрос:
У меня есть несколько структур с полем «DateLastModified», например:
type Thing struct {
DateLastModified time.Time
...
}
type Thing2 struct {
DateLastModified time.Time
...
}
У меня есть срезы или карты каждой из этих структур:
things := []Thing{...}
thing2s := []Thing2{...}
//or
things := make(map[string]Thing)
thing2s := make(map[string]Thing2)
Что я хотел бы сделать, так это отфильтровать каждый из этих фрагментов или карт на их DateLastModified
поле.
Это просто реализовать базовым способом, но мне интересно узнать больше о Go.
Мне интересно вот что: есть ли способ реализовать этот фильтр таким образом, чтобы я мог сделать что-то вроде:
filteredThings := filterSliceOnTime(things, someTimeToFilterOn)
filtered2Things := filterSliceOnTime(thing2s, someTimeToFilterOn)
//or
filteredThings := filterMapOnTime(things, someTimeToFilterOn)
filtered2Things := filterMapOnTime(thing2s, someTimeToFilterOn)
Дело в том, что я пытаюсь понять, как уменьшить избыточный код, поскольку во всех этих структурах есть это поле DateLastModified.
Спасибо!
Ответ №1:
Go не имеет понятия о ко-/контра-дисперсии, поэтому filterXOnTime
не сможет оперировать срезами []V
или картами map[K]V
структур различных базовых V
s.
С интерфейсом
Что вы можете сделать, так это объявить интерфейс I
, который отображает общее поведение, и чтобы обе структуры реализовали этот интерфейс:
type Modifiable interface {
GetDateLastModified() time.Time
}
type Thing struct {
DateLastModified time.Time
...
}
func (t Thing) GetDateLastModified() time.Time {
return t.DateLastModified
}
type Thing2 struct {
DateLastModified time.Time
...
}
func (t Thing2) GetDateLastModified() time.Time {
return t.DateLastModified
}
На этом этапе у вас могут быть срезы и карты I
, и ваша функция фильтра может работать (принимать и возвращать) с ними:
func filterSliceOnTime(modifiables []Modifiable, someTimeToFilterOn time.Time) []Modifiable {
var filtered []Modifiable
for _, m := range modifiables {
if m.GetDateLastModified.After(someTimeToFilterOn) {
filtered = append(filtered, m)
}
}
return filtered
}
Эффективность этого подхода несколько ограничена тем фактом, что []ThingX
[]Modifiable
для использования «общей» функции вам необходимо переназначить и наоборот.
С помощью вспомогательной функции
Чтобы уменьшить проблемы с обслуживанием вышеприведенного решения, вы можете вместо этого использовать вспомогательную функцию фильтра, которая работает с одним элементом, поэтому вам не нужно сопоставлять сложные типы взад и вперед:
func checkOne(v Modifiable, filterOn time.Time) bool {
return v.GetDateLastModified().After(filterOn)
}
func main() {
a := make(map[string]Thing, 0)
a["foo"] = Thing{time.Now()}
filtered := make(map[string]Thing, 0)
for k, v := range a {
if checkOne(v, time.Now().Add(-2*time.Hour)) {
filtered[k] = v
}
}
fmt.Println(filtered) // map[foo:{blah}]
}
Перейдите 1.18 и введите параметры
С Go 1.18 (начало 2022 года) и внедрением дженериков вы сможете вместо этого напрямую использовать срезы и карты. Вам все равно придется объявить интерфейс, чтобы обеспечить надлежащее ограничение для параметра типа, и структурам все равно придется его реализовать.
С текущим эскизным проектом это может выглядеть так:
func filterSliceOnTime[T Modifiable](s []T, filterOn time.Time) []T {
var filtered []T
for _, v := range s {
if v.GetDateLastModified().After(filterOn) {
filtered = append(filtered, v)
}
}
return filtered
}
Игровая площадка Go2: https://go2goplay.golang.org/p/FELhv0NSr5A
Комментарии:
1. Это именно то руководство, которое я надеялся получить. Большое спасибо, что научили меня!
Ответ №2:
Вы можете извлечь базовую структуру , в которой есть поле DateLastModified time.Time
и методы, такие как isBefore(start)
, isAfter(end)
, isBetween(start, end)