Проверка на пустой список

Не используйте (not (empty? coll)). Вместо этого следует применять (seq coll).

seq возвращает nil, если коллекция пустая, иначе – возвращает саму эту коллекцию.

Примечание переводчика: смотрите, как реализована функция empty?: (defn empty? [coll] (not (seq coll))). Это значит, что конструкция (not (empty? coll)) будет преобразована в (not (not (seq coll))). Естественно, здесь лучше упростить до просто (seq coll).

Выравнивание результата map по одному уровню

Если результат вызова функцииmap возвращает список списков, то для “выравнивания” списков следует использовать mapcat вместо (apply (concat (map...))):

(map list (range 10))
 ;; => ((0) (1) (2) (3) (4) (5) (6) (7) (8) (9))

 (mapcat list (range 10))
 ;; => (0 1 2 3 4 5 6 7 8 9)

Примечание переводчика: вот реализация mapcat: (defn mapcat [f & colls] (apply concat (apply map f colls))). Т.е. mapcat и есть (apply concat (map ....)).

Отображения (maps) являются функциями

Отображения, как и ключевые слова, являются функциями.

Для доступа к значениям отображения можно испозовать ключевое слово (keyword) вместо функции, само отображение или функцию get:

(def m {:a 1 :b 2})

 (m :a) ;; => 1
 (:a m) ;; => 1
 (get m :a) ;; => 1

Здесь наиболее предпочтительный вариант – средний, т.е. для доступа к значениям лучше использовать ключевое слово (keyword). Такой подход защитит вас от NullPointerException. Ключевое слово не может быть nil, в то время, как отображение – может.

(nil :a) ;; CompilerException java.lang.IllegalArgumentException: Can't call nil, form: (nil :a)
 (:a nil) ;; => nil

Множества – тоже функции

Как и отображения, множества также являются функциями. Их можно объявить при помощи функции set или #{}.

(def key-set #{:a :b})

 (key-set :a) ;; => :a
 (key-set :b) ;; => :b
 (key-set :c) ;; => nil

 (:a key-set) ;; => :a
 (:b key-set) ;; => :b
 (:c key-set) ;; => nil

Множество возвращает значение, если оно там есть, иначе – nil. Это свойство поможет нам делать некоторые интересные вещи.

Не используйте contains? на множествах

Если у вас есть множество, не используйте contains? чтобы проверить наличие элемента в нем. Просто используйте само множество как функцию.

(def key-set #{:a :b})

 ;; Не надо так
 (contains? key-set :a) ;; => true

 ;; Лучше вот так
 (key-set :a) ;; => :a

Примечание переводчика: здесь я не согласен с автором. Дело в том, что contains? корректно работает и на значениях nil, например (contains? nil :abc) просто вернёт false. Но если мы вызовем (myset :abc), а myset будет равен nil, то возникнет исключение. Правда (:abc myset) отработает вполне нормально.

Идиома (every? key-set (keys my-map))

(def key-set #{:a :b})

 (every? key-set [:a :b])

Эта идиома очень удобна для проверки JSON-ответов. С её помощью можно проверить, что в полученном ответе отображение содержит только ключи из некоторого множества.

(def endpoint-response {:name "Fat Tony"
                         :address-line-1 "123 Fake St."
                         :zip "60606"})

 (def expected-keys #{:name :address-line-1 :zip})

 (every? expected-keys (keys endpoint-response)) ;; => true

Conditionally Assoc’ing Onto A Map

Если вы используете на отображении макрос -> or ->>, и вам нужно выполнить assoc/dissoc в зависимости от некоторого условия, используйте cond->:

(def m {:a 1})

 (cond-> m
   :a (assoc :b 2)) ;; => {:a 1, :b 2}

Примечание переводчика: думаю, здесь следует пояснить поведение макроса cond->. Как и ->, первым аргументом он принимает некоторое начальное значение, а затем – список пар “значение / операция”. Операция над аргументом cond-> выполняется только в том случае, если значение из пары является истиной. Таким образом, в приведенном примере операция (assoc :b 2) выполнилась, потому что в паре :a (assoc :b 2) значение :a является истиной. Если бы там было false (assoc :b 2), то (assoc :b 2) не выполнилось бы.

Оригинал статьи – здесь. Перевод – Дмитрий Бушенко.