Статьи

Clojure: преобразование массива / набора в хэш-карту

Когда я  внедрял алгоритм Elo Rating  несколько недель назад, мне нужно было создать базовый рейтинг для каждой команды.

Я начал с набора команд, которые выглядели так:

(def teams #{ "Man Utd" "Man City" "Arsenal" "Chelsea"})

и я хотел преобразовать это в карту от команды до их рейтинга, например

Man Utd -> {:points 1200}
Man City -> {:points 1200}
Arsenal -> {:points 1200}
Chelsea -> {:points 1200}

Я прочитал документацию по  array-map , функции, которую можно использовать для преобразования набора пар в карту, и казалось, что он может сработать.

Я начал с создания массива пар, используя  mapcat :

> (mapcat (fn [x] [x {:points 1200}]) teams)
("Chelsea" {:points 1200} "Man City" {:points 1200} "Arsenal" {:points 1200} "Man Utd" {:points 1200})

массив-карта  строит карту из пар значений, например

> (array-map "Chelsea" {:points 1200} "Man City" {:points 1200} "Arsenal" {:points 1200} "Man Utd" {:points 1200})
("Chelsea" {:points 1200} "Man City" {:points 1200} "Arsenal" {:points 1200} "Man Utd" {:points 1200})

Так как у нас есть коллекция пар, а не отдельных пар, нам нужно также использовать   функцию apply :

> (apply array-map ["Chelsea" {:points 1200} "Man City" {:points 1200} "Arsenal" {:points 1200} "Man Utd" {:points 1200}])
{"Chelsea" {:points 1200}, "Man City" {:points 1200}, "Arsenal" {:points 1200}, "Man Utd" {:points 1200}}

И если мы соберем все это вместе, мы получим следующее:

> (apply array-map (mapcat (fn [x] [x {:points 1200}]) teams))
{"Man Utd" {:points 1200}, "Man City" {:points 1200}, "Arsenal" {:points 1200}, "Chelsea" {:points 1200}}

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

> (mapcat vector teams (repeat {:points 1200}))
("Chelsea" {:points 1200} "Man City" {:points 1200} "Arsenal" {:points 1200} "Man Utd" {:points 1200})

И если мы  вернем  код применения массива-карты обратно, мы все равно получим желаемый результат:

> (apply array-map (mapcat vector teams (repeat {:points 1200})))
{"Chelsea" {:points 1200}, "Man City" {:points 1200}, "Arsenal" {:points 1200}, "Man Utd" {:points 1200}}

В качестве альтернативы мы могли бы использовать  Assoc  следующим образом:

> (apply assoc {} (mapcat vector teams (repeat {:points 1200})))
{"Man Utd" {:points 1200}, "Arsenal" {:points 1200}, "Man City" {:points 1200}, "Chelsea" {:points 1200}}

Я также натолкнулся  на  функцию, которая казалась полезной, но взял коллекцию векторов:

> (into {} [["Chelsea" {:points 1200}] ["Man City" {:points 1200}] ["Arsenal" {:points 1200}] ["Man Utd" {:points 1200}] ])

Поэтому нам нужно изменить код для использования  map  вместо  mapcat :

> (into {} (map vector teams (repeat {:points 1200})))
{"Chelsea" {:points 1200}, "Man City" {:points 1200}, "Arsenal" {:points 1200}, "Man Utd" {:points 1200}}

Тем не менее, моя любимая версия до сих пор использует   функцию zipmap :

> (zipmap teams (repeat {:points 1200}))
{"Man Utd" {:points 1200}, "Arsenal" {:points 1200}, "Man City" {:points 1200}, "Chelsea" {:points 1200}}

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