Прощай, бойлерплейт - `samber/lo` привносит Lodash-магию в Go

21 Jun, 2026
21,346
🔱 944
👥 75

Знакомая ситуация: пишешь на Go, и все прекрасно, но вот доходит дело до обработки коллекций — слайсов, мап. И тут начинается: бесконечные циклы for, проверки ошибок, ручная фильтрация, маппинг... Вроде бы и стандартная библиотека что-то дает, но часто хочется чего-то более лаконичного, функционального, того, что позволяет писать меньше кода и выражать свою мысль яснее. Особенно если вы пришли из мира JavaScript и скучаете по удобствам Lodash.

Что ж, коллеги, сегодня у меня для вас отличная новость! Я наткнулся на проект, который, возможно, изменит ваш подход к работе с коллекциями в Go. Встречайте — samber/lo, библиотека, которая обещает привнести в Go «Lodash-стиль» благодаря магии дженериков Go 1.18+.

Что такое samber/lo и зачем он нужен?

samber/lo — это утилитарная библиотека, созданная для упрощения рутинных операций со слайсами, мапами, строками, каналами и функциями. Ее основная цель — сократить количество шаблонного кода, сделать его более читаемым и выразительным. Представьте, что у вас есть набор данных, и вам нужно его отфильтровать, трансформировать, сгруппировать или найти уникальные элементы. Без lo вы, скорее всего, напишете несколько циклов for с условными операторами. С lo — это будет одна-две строчки кода, интуитивно понятные и легко читаемые.

Логотип samber/lo

Разработчик samber/lo прямо указывает, что некоторые функции могут пересекаться с пакетами slices и maps из стандартной библиотеки Go. Однако, по его мнению, lo предлагает гораздо больше ценных абстракций, которые значительно расширяют возможности и удобство. И я с ним полностью согласен! Библиотека не имеет внешних зависимостей, кроме стандартной библиотеки Go, что является огромным плюсом.

Реклама

Ключевые возможности, которые упростят вашу жизнь

Давайте рассмотрим несколько примеров, чтобы понять, насколько lo может быть полезен в повседневной разработке.

1. Трансформация и фильтрация данных: Map и Filter

Это, пожалуй, одни из самых востребованных операций. Нужно преобразовать один слайс в другой или отфильтровать его по какому-то условию? lo делает это элегантно.

Представьте, что у вас есть слайс чисел, и вы хотите получить слайс их строковых представлений:

import (
    "github.com/samber/lo"
    "strconv"
)

numbers := []int64{1, 2, 3, 4}
strings := lo.Map(numbers, func(x int64, index int) string {
    return strconv.FormatInt(x, 10)
})
// strings: []string{"1", "2", "3", "4"}

Или, например, оставить только четные числа:

import "github.com/samber/lo"

evenNumbers := lo.Filter([]int{1, 2, 3, 4}, func(x int, index int) bool {
    return x%2 == 0
})
// evenNumbers: []int{2, 4}

Согласитесь, это гораздо чище и понятнее, чем писать вручную цикл for с append и проверками!

2. Работа с уникальными значениями и группировка: Uniq и GroupBy

Часто возникает необходимость удалить дубликаты или сгруппировать элементы по какому-либо критерию. lo и здесь приходит на помощь.

Удаляем дубликаты:

import "github.com/samber/lo"

uniqValues := lo.Uniq([]int{1, 2, 2, 1})
// uniqValues: []int{1, 2}

Группируем элементы. Например, по остатку от деления на 3:

import "github.com/samber/lo"

groups := lo.GroupBy([]int{0, 1, 2, 3, 4, 5}, func(i int) int {
    return i%3
})
// groups: map[int][]int{0: {0, 3}, 1: {1, 4}, 2: {2, 5}}

Это невероятно удобно для предварительной обработки данных перед дальнейшим анализом или отображением.

3. Извлечение данных из мап: Keys и Values

Работая с мапами, мы часто хотим получить либо все ключи, либо все значения. lo предоставляет для этого простые функции.

import "github.com/samber/lo"

m := map[string]int{"foo": 1, "bar": 2}

keys := lo.Keys(m)
// keys: []string{"foo", "bar"} (порядок не гарантируется)

values := lo.Values(m)
// values: []int{1, 2} (порядок не гарантируется)

Есть также варианты UniqKeys и UniqValues для получения только уникальных ключей или значений из нескольких мап.

4. Конкурентность и каналы: Async, Buffer, ChannelDispatcher

Go славится своей конкурентной моделью, и lo не обходит эту тему стороной. Здесь есть весьма интересные хелперы для работы с горутинами и каналами.

Например, можно легко запустить функцию в отдельной горутине и получить ее результат через канал:

import (
    "github.com/samber/lo"
    "time"
)

ch := lo.Async(func() error {
    time.Sleep(1 * time.Second) // Долгая операция
    return nil
})

// Делаем что-то еще, пока горутина работает

err := <-ch // Ждем результат
// err: nil (через 1 секунду)

Для более сложных сценариев есть Buffer для чтения N элементов из канала (с таймаутом или без), что идеально подходит для пакетной обработки данных, например, из очередей сообщений. А ChannelDispatcher позволяет распределять сообщения из одного канала по нескольким дочерним каналам, реализуя различные стратегии балансировки.

Технические детали: дженерики в действии и производительность

Как я уже упоминал, samber/lo полностью построен на дженериках Go 1.18+. Это означает, что вы получаете типобезопасные и производительные утилиты без необходимости писать код для каждого типа данных. Больше никаких interface{} и рефлексии там, где она не нужна!

Кстати, о производительности: в README проекта приведены бенчмарки, которые показывают, что lo.Map работает почти так же быстро, как обычный цикл for, и значительно обгоняет реализации, основанные на рефлексии (например, go-funk). Это очень важный момент, ведь часто при использовании подобных библиотек возникают опасения по поводу накладных расходов. samber/lo же показывает отличные результаты.

Что особенно радует, так это отсутствие внешних зависимостей. Это значит, что вы не тащите в свой проект кучу лишнего кода, и ваш бинарник остается компактным. Библиотека следует принципам SemVer, и разработчик обещает отсутствие ломающих изменений до версии 2.0.0.

Практическое применение: где lo сияет?

  • Обработка API-ответов: Часто API возвращают сложные структуры данных. lo упрощает их трансформацию и фильтрацию для дальнейшего использования.
  • Очистка и подготовка данных: Удаление пустых значений, дубликатов, приведение к нужному формату — все это становится тривиальным.
  • Разработка CLI-утилит: Быстрое прототипирование и обработка входных данных.
  • Работа с базами данных: Постобработка результатов запросов, агрегация.
  • Конкурентные задачи: Управление потоками данных через каналы, параллельное выполнение операций.

В целом, lo пригодится везде, где есть необходимость в манипуляциях с коллекциями, и где хочется писать более чистый, функциональный и легко поддерживаемый код.

Выводы: стоит ли попробовать?

Мой однозначный ответ — да! samber/lo — это не просто еще одна утилитарная библиотека, это качественно новый подход к работе с коллекциями в Go, ставший возможным благодаря дженерикам. Она позволяет писать код, который:

  1. Лаконичен: Меньше строчек, больше смысла.
  2. Читаем: Намерения кода становятся очевидными с первого взгляда.
  3. Производителен: Без компромиссов в скорости.
  4. Типобезопасен: Компилятор всегда на вашей стороне.

Если вы устали от бойлерплейта при работе со слайсами и мапами, или если вы цените функциональный стиль программирования, то samber/lo станет отличным дополнением к вашему инструментарию Go-разработчика. Загляните в репозиторий и попробуйте сами. Уверен, вы не пожалеете!

А какие утилитарные библиотеки для Go используете вы? Делитесь в комментариях!

🍪 Мы используем файлы cookie и сервис аналитики Яндекс.Метрика, чтобы сайт работал лучше. Продолжая пользоваться devtrends.ru, вы соглашаетесь с обработкой данных согласно Политике конфиденциальности.