У меня есть метод ниже, в котором я создаю три разных объекта структуры для проверки в качестве ключа на моей карте name
. Если я могу найти значение, я возвращаюсь обратно, иначе я возвращаю пустую строку и статус.
func (r *clientRepo) GetName(id int64, name map[models.LocaleInfo]string, clientId int32, code models.Locale) (string, bool) {
localeInfo := models.LocaleInfo{
Locale: code,
GroupID: int(clientId),
}
localeInfoUS := models.LocaleInfo{
Locale: "US",
GroupID: int(clientId),
}
localeInfoGB := models.LocaleInfo{
Locale: "GB",
GroupID: int(clientId),
}
value := ""
ok := false
if value, ok = name[localeInfo]; !ok {
if value, ok = name[localeInfoUS]; !ok {
if value, ok := name[localeInfoGB]; !ok {
errs.Newf("Unable to find locale for ProductId: %d", id)
return value, ok
}
}
}
return value, ok
}
Есть ли способ улучшить приведенный выше код, где мне не нужно хранить отдельную строку для каждой информации о локали США и ГБ. Мой блок if тоже выглядит очень вложенным, и я думаю, что его тоже можно значительно улучшить.
Специальную форму «запятая-ок» можно использовать только в задании, поэтому сделать ее более компактной нельзя.
Что вы можете сделать, так это написать служебную функцию, которая использует цикл for для проверки любого количества ключей.
Используя дженерики Go 1.18, вы можете заставить его работать с любым типом карты:
func findKeys[K comparable, V any](m map[K]V, keys ...K) (v V, ok bool) {
for _, key := range keys {
if v, ok = m[key]; ok {
return
}
}
return
}
Тестирование:
m := map[int]string{
1: "one",
2: "two",
3: "three",
4: "four",
}
fmt.Println(findKeys(m, 5, 6, 4))
fmt.Println(findKeys(m, 1, 2, 6))
fmt.Println(findKeys(m, 6, 8))
Какие выходы (попробуйте на Игровая площадка):
four true
one true
false
Примечание: если вы используете Go до версии Go 1.18, просто используйте неуниверсальную версию findKeys()
:
func findKeys(m map[models.LocaleInfo]string, keys ...models.LocaleInfo) (v string, ok bool) {
for _, key := range keys {
if v, ok = m[key]; ok {
return
}
}
return
}
Вы можете использовать это findKeys()
следующим образом:
value, ok := findKeys(name, localeInfo, localeInfoUS, localeInfoGB)
if !ok {
errs.Newf("Unable to find locale for ProductId: %d", id)
}
return value, ok
Также обратите внимание, что если вы используете неуниверсальную версию, поскольку она в любом случае будет ограничена вашим типом карты, вы можете переместить в нее больше кода: она может обрабатывать создание ключа, поэтому вам нужно передать только значения локали string
. (и clientId
):
func findKeys(names map[models.LocaleInfo]string, clientId int, locales ...string) (v string, ok bool) {
key := models.LocaleInfo{GroupID: clientId}
for _, locale := range locales {
key.Locale = locale
if v, ok = names[key]; ok {
return
}
}
return
}
Вызов это выглядит так:
value, ok := findKeys(names, int(clientId), code, "US", "GB")
См. соответствующий вопрос: Проверьте, существует ли ключ на нескольких картах в одном условии
@rosed Тогда просто используйте неуниверсальную версию findKeys()
, см. отредактированный ответ.
Понятно. Это имеет смысл. Так что у меня по-прежнему будет три разных объекта localeInfo
, так как это просто блок, если он улучшен. Верно?
@rosed У вас может быть столько ключей, сколько вы хотите. Также обратите внимание, что если вы используете неуниверсальную версию, которая в любом случае ограничена вашим типом карты, вы можете переместить создание ключа в служебную функцию, поэтому ее использование будет намного проще. Смотрите отредактированный ответ.
Быстрый вопрос - range locale {
это должно быть locales
верно? А также получение ошибки в key.Locale = locale
bcoz key.Locale
— это объект.
@rosed Да, locale
=> locales
была опечатка, но обратите внимание, что в этой версии locales
— это string
срез, а не срез объекта! Итак, key.Locale = locale
действителен.
Спасибо за ваше предложение. Я все еще работаю с 1.17. Как мне добиться этого лучше, используя 1.17? В моем случае карта такая же, но разные ключи.