Clojure предоставляет функции get и get-in для возврата значений из карты и функцию select-keys для возврата новой карты только с указанными ключами. Clojure не предоставляет функцию, которая возвращает список значений; однако очень легко создать такую функцию (которую я называю select-values). Если у вас есть возможность выбора значений, становится очень легко создать функцию, которая применяет функцию к выбранным значениям (которую я называю apply-values).
Функция select-keys возвращает карту, содержащую только записи указанных ключей. В следующем (вставленном) сеансе REPL показано несколько вариантов поведения клавиш выбора.
user=> (select-keys {:a 1 :b 2} [:a]) {:a 1} user=> (select-keys {:a 1 :b 2} [:a :b]) {:b 2, :a 1} user=> (select-keys {:a 1 :b 2} [:a :b :c]) {:b 2, :a 1} user=> (select-keys {:a 1 :b 2} []) {} user=> (select-keys {:a 1 :b 2} nil) {}
Функция клавиш выбора полезна во многих случаях; однако иногда вас интересует только выбор значений определенных ключей на карте. Простое решение состоит в том, чтобы вызвать select-keys и затем vals. Ниже вы можете найти результаты применения этой идеи.
user=> (def select-values (comp vals select-keys)) #'user/select-values user=> (select-values {:a 1 :b 2} [:a]) (1) user=> (select-values {:a 1 :b 2} [:a :b]) (2 1) user=> (select-values {:a 1 :b 2} [:a :b :c]) (2 1) user=> (select-values {:a 1 :b 2} []) nil user=> (select-values {:a 1 :b 2} nil) nil
Реализация select-values из приведенного выше может быть достаточной для того, что вы делаете, но есть две вещи, на которые стоит обратить внимание: в случаях, когда вы можете ожидать пустой список, вы видите nil; и значения находятся не в том порядке, в котором ключи были указаны. Учитывая, что (стандартные) карты не отсортированы, вы не можете быть уверены в порядке значений.
(бок . Примечание: Если вы обеспокоены микросекундами, это также было сообщено , что выберите клавиши немного медленно / мусор тяжелый)
Альтернативное определение отдельных значений использует
уменьшить функцию и тянет значения по ключу и постепенно строит (вектор) результат.
user=> (defn select-values [map ks] (reduce #(conj %1 (map %2)) [] ks)) #'user/select-values user=> (select-values {:a 1 :b 2} [:a]) [1] user=> (select-values {:a 1 :b 2} [:a :b]) [1 2] user=> (select-values {:a 1 :b 2} [:a :b :c]) [1 2 nil] user=> (select-values {:a 1 :b 2} []) [] user=> (select-values {:a 1 :b 2} nil) []
Новая функция select-values возвращает значения по порядку и возвращает пустой вектор в случаях, когда предыдущие примеры возвращали ноль, но у нас есть новая проблема: указанные ключи, которых нет на карте, теперь включаются в вектор как ноль , Эта проблема легко решается путем добавления вызова
функции удаления .
Реализация, которая включает удаление nils, может быть найдена ниже.
user=> (defn select-values [map ks] (remove nil? (reduce #(conj %1 (map %2)) [] ks))) #'user/select-values user=> (select-values {:a 1 :b 2} [:a]) (1) user=> (select-values {:a 1 :b 2} [:a :b]) (1 2) user=> (select-values {:a 1 :b 2} [:a :b :c]) (1 2) user=> (select-values {:a 1 :b 2} []) () user=> (select-values {:a 1 :b 2} nil) ()
Не существует «правильной» реализации для select-values. Если вы не заботитесь о порядке и nil — разумное возвращаемое значение: первая реализация — правильный выбор из-за ее краткого определения. Если вы заботитесь о порядке и производительности: вторая реализация может быть правильным выбором. Если вы хотите что-то, что следует принципу наименьшего удивления: третья реализация, вероятно, является правильным выбором Вы должны решить, что лучше для вашего контекста. На самом деле, вот еще несколько реализаций, которые могут быть лучше в зависимости от вашего контекста.
user=> (defn select-values [m ks] (map #({:a 1 :b 2} %) ks)) #'user/select-values user=> (select-values {:a 1 :b 2} [:a]) (1) user=> (select-values {:a 1 :b 2} [:a :b :c]) (1 2 nil) user=> (defn select-values [m ks] (reduce #(if-let [v ({:a 1 :b 2} %2)] (conj %1 v) %1) [] ks)) #'user/select-values user=> (select-values {:a 1 :b 2} [:a]) [1] user=> (select-values {:a 1 :b 2} [:a :b :c]) [1 2]
Извлечение значений из карты полезно, но обычно это не конечная цель. Если вы обнаружите, что извлекаете значения из карты, вполне вероятно, что вы захотите применить функцию к извлеченным значениям. Имея это в виду, я обычно определяю функцию apply-values, которая возвращает результат применения функции к значениям, возвращаемым указанными ключами.
Хорошим примером этого является возврат итогов для позиции, представленной в виде карты. Учитывая карту, в которой указана позиция стоимостью 5 долларов США и имеющая количество 4, вы можете использовать (* количество цены), чтобы определить общую цену позиции.
Используя нашу ранее определенную функцию select-values, мы можем сделать работу самостоятельно, как показано в примере ниже.
user=> (let [[price quantity] (select-values {:price 5 :quantity 4 :upc 1123} [:price :quantity])] (* price quantity)) 20
Пример выше работает отлично; однако применение функции к значениям карты кажется довольно общей операцией, которую можно легко извлечь в ее собственную функцию (функция apply-values). Пример ниже показывает определение и использование моего определения apply-values.
user=> (defn apply-values [map f & ks] (apply f (select-values map ks))) #'user/apply-values user=> (apply-values {:price 5 :quantity 4 :upc 1123} * :price :quantity) 20
Я считаю, что при написании приложений Clojure полезны клавиши выбора, значения выбора и значения применений. Если вы обнаружите, что вам нужны эти функции, не стесняйтесь использовать их в своем собственном коде. Тем не менее, вы, вероятно, захотите проверить комментарии — я уверен, что кто-то с большим опытом работы с Clojure предоставит превосходные реализации.
С http://blog.jayfields.com/2011/01/clojure-select-keys-select-values-and.html