Преобразование списка списков в карту списков списков в Clojure - PullRequest
2 голосов
/ 15 октября 2010

У меня есть список списков, и я хотел бы получить его на карте, где ключ является одним из общих значений в списках (имя животного в этом примере).Я знаю, как использовать в {} и для создания карты из списка, но это не совсем то, что я хочу.Я хочу, чтобы ключ (имя животного) на карте ссылался на список списков этих значений.

Я создал список списков

(def animals (list '("tiger" "fur" "yellow & black stripes") '("tiger" "weight" "150") '("tiger" "home" "India") '("elephant" "skin" "gray") '("elephant" "weight" "1500") '("elephant" "home" "Africa") '("frog" "skin" "green") '("frog" "diet" "insects")))

животных

(("tiger" "fur" "yellow & black stripes") ("tiger" "weight" "150") ("tiger" "home" "India") ("elephant" "skin" "gray") ("elephant" "weight" "1500") ("elephant" "home" "Africa") ("frog" "skin" "green") ("frog" "diet" "insects"))

Это не совсем то, что я хочу (но почти)

(def animal-map (into {} (for [[name attribute value] animals] [name (list attribute value)])))

Этот результаттолько в одном списке для каждой клавиши, когда мне нужно несколько списков для каждой клавиши

карта животных (карта списков)

{"tiger" ("home" "India"), "elephant" ("home" "Africa"), "frog" ("diet" "insects")}

Это хочу, чтобы я хотел закончить.

карта животных (карта списка списков)

{"tiger" ("fur" "yellow & black stripes") ("weight" "150") ("home" "India"),   
"elephant" ("skin" "gray") ("weight" "1500") ("home" "Africa"),   
"frog" ("skin" "green") ("diet" "insects")}  

Ответы [ 3 ]

8 голосов
/ 16 октября 2010

Что касается вашего желаемого результата, последняя карта, которую вы разместили, не делает то, что вы думаете, что делает. На этой карте:

{"tiger" ("fur" "yellow & black stripes") ("weight" "150") ("home" "India")}

Это не карта с одним ключом и тремя значениями, это карта с двумя ключами ("tiger" и ("weight" "150")) и двумя значениями.

Следующее не отвечает на ваш вопрос напрямую, но говорит о идиоматическом Clojure. В Clojure вам будет легче, если вы выберете правильные структуры данных для текущей работы.

Для исходных данных вам, вероятно, следует использовать векторы вместо списков, если у вас нет веских оснований для использования списков. Векторы работают лучше для многих операций (например, произвольный доступ); вам не нужно заключать в кавычки вектор, как вы делаете список; и векторы сигнализируют о том, что вы имеете дело с данными, а не с кодом.

Списки подходят, если ваши данные должны расти спереди, а не сзади, или если вы генерируете исходный код в качестве данных (например, в макросах).

[["tiger" "fur" "yellow & black stripes"]
 ["tiger" "weight" "150"]
 ["tiger" "home" "India"]
 ...]

Для получения окончательных данных рассмотрите возможность использования карты строк в картах вместо карты строк в списках. С помощью assoc-in легко создавать карты карт, потому что он создает промежуточные карты для вас. Затем вы можете получить доступ к подключам ваших данных, используя get-in.

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

user> (def animal-map (reduce (fn [m [animal k v]]
                                (assoc-in m [animal k] v))
                              {} animals))
#'user/animal-map
user> animal-map
{"frog" {"diet" "insects", "skin" "green"}, 
 "elephant" {"home" "Africa", "weight" "1500", "skin" "gray"}, 
 "tiger" {"home" "India", "weight" "150", "fur" "yellow & black stripes"}}

user> (get-in animal-map ["frog" "diet"])
"insects"

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

Если ваши ключи являются ключевыми словами, вам даже не нужно get-in, вы можете использовать ключевые слова как функции напрямую или использовать ->. Вы не можете сделать это со строками.

user> (def animal-keyword-map (reduce (fn [m [animal k v]]
                                        (assoc-in m [(keyword animal) (keyword k)] v))
                                      {} animals))
#'user/animal-keyword-map
user> animal-keyword-map
{:frog {:diet "insects", :skin "green"}, 
 :elephant {:home "Africa", :weight "1500", :skin "gray"}, 
 :tiger {:home "India", :weight "150", :fur "yellow & black stripes"}}
user> (:weight (:tiger animal-keyword-map))
"150"
user> (-> animal-keyword-map :tiger :weight)
"150"
0 голосов
/ 15 октября 2010

, чтобы избежать более поздних записей, заменяющих предыдущие, вам нужно создать группу карт и затем объединить их контролируемым образом. Ура! у нас есть merge-with только для этой работы!

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

(map #(hash-map (first %) (rest %)) animals)

({"tiger" ("fur" "yellow & black stripes")} 
 {"tiger" ("weight" "150")} 
 {"tiger" ("home" "India")} 
 {"elephant" ("skin" "gray")} 
 {"elephant" ("weight" "1500")} 
 {"elephant" ("home" "Africa")} 
 {"frog" ("skin" "green")} 
 {"frog" ("diet" "insects")})

тогда давайте попробуем просто объединить их с list, потому что это первое, что приходит на ум.

(apply merge-with list (map #(hash-map (first %) (rest %)) animals))

{"frog" (("skin" "green") ("diet" "insects")), "elephant" ((("skin" "gray") 
("weight"    "1500")) ("home" "Africa")), "tiger" ((("fur" "yellow & black stripes") 
("weight" "150")) ("home" "India"))}

Это больше похоже на то, что вложено слишком глубоко. чтобы не беспокоиться, мы можем предотвратить вложение, переместив создание списка во внутреннюю карту:

(apply merge-with concat (map #(hash-map (first %) (list (rest %))) animals))

{"frog" (("skin" "green") ("diet" "insects")), "elephant" (("skin" "gray") ("weight" 
"1500") ("home" "Africa")), "tiger" (("fur" "yellow & black stripes") ("weight" "150")
 ("home" "India"))}

Я не смогу сопоставить желаемый результат точно, потому что карта может иметь только одно значение для ключа. Для «тигра» необходимо сопоставить список пар. вместо более чем одной пары.

0 голосов
/ 15 октября 2010

Соединяя ваш список с {}, вы заменяете дублирующиеся ключи.

(reduce (fn [m [k & vals]] (assoc m k (conj (m k) vals))) {} animals)
...