Как перебрать ключи и значения на карте? - PullRequest
59 голосов
/ 14 июля 2011

У меня есть следующая карта, которую я хочу повторить:

(def db {:classname "com.mysql.jdbc.Driver" 
         :subprotocol "mysql" 
         :subname "//100.100.100.100:3306/clo" 
         :username "usr" :password "pwd"})

Я пробовал следующее, но вместо того, чтобы печатать ключ и значение один раз , он многократно печатаетключ и значения в виде различных комбинаций:

(doseq [k (keys db) 
        v (vals db)] 
  (println (str k " " v)))

Я нашел решение, но Брайан (см. ниже) гораздо более логичен.

(let [k (keys db) v (vals db)] 
  (do (println (apply str (interpose " " (interleave k v))))))

Ответы [ 4 ]

90 голосов
/ 14 июля 2011

Это ожидаемое поведение.(doseq [x ... y ...]) будет перебирать каждый элемент в y для каждого элемента в x.

Вместо этого вы должны перебирать саму карту один раз.(seq some-map) вернет список векторов из двух элементов, по одному для каждой пары ключ / значение на карте.(На самом деле они clojure.lang.MapEntry, но ведут себя как векторы из 2 элементов.)

user> (seq {:foo 1 :bar 2})
([:foo 1] [:bar 2])

doseq может выполнять итерации по этому результату, как и любой другой.Как и большинство функций в Clojure, которые работают с коллекциями, doseq внутренне вызывает seq вашей коллекции, прежде чем выполнять итерацию по ней.Таким образом, вы можете просто сделать это:

user> (doseq [keyval db] (prn keyval))
[:subprotocol "mysql"]
[:username "usr"]
[:classname "com.mysql.jdbc.Driver"]
[:subname "//100.100.100.100:3306/clo"]
[:password "pwd"]

Вы можете использовать key и val, или first и second, или nth, или get, чтобы получить ключи изначения из этих векторов.

user> (doseq [keyval db] (prn (key keyval) (val keyval)))
:subprotocol "mysql"
:username "usr"
:classname "com.mysql.jdbc.Driver"
:subname "//100.100.100.100:3306/clo"
:password "pwd"

Более кратко, вы можете использовать деструктурирование, чтобы привязать каждую половину записей карты к некоторым именам, которые вы можете использовать внутри формы doseq.Это идиоматично:

user> (doseq [[k v] db] (prn k v))
:subprotocol "mysql"
:username "usr"
:classname "com.mysql.jdbc.Driver"
:subname "//100.100.100.100:3306/clo"
:password "pwd"
16 голосов
/ 13 декабря 2016

Вы можете просто сделать

(map (fn [[k v]] (prn k) (prn v)) {:a 1 :b 2})

Результат:

:a
1
:b
2

Это то, что вы искали?

4 голосов
/ 14 июля 2011

Просто короткое дополнение к ответу Брайана:

Ваша оригинальная версия также может быть написана следующим образом.

(doseq [[k v] (map vector (keys db) (vals db))]
  (println (str k " " v)))

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

0 голосов
/ 08 ноября 2018

Не совсем понятно, пытаетесь ли вы решить что-то, кроме распечатки значений (побочных эффектов), и если это все, к чему вы стремитесь, то я думаю, что решение doseq, приведенное выше, будет самым идиоматичным. Если вы хотите выполнить некоторые операции с ключами и значениями карты и вернуть тот же тип структуры данных, вам следует взглянуть на reduce-kv, для которого вы можете найти документы для здесь

Как и reduce, reduce-kv принимает функцию, начальное значение / аккумулятор и некоторые данные, но в этом случае данные представляют собой карту, а не последовательность. Функция получает три аргумента: аккумулятор, ключ тока и значение тока. Если вы действительно хотите выполнить какое-то преобразование данных и вернуть некоторые данные, это может показаться мне подходящим инструментом для работы.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...