Преобразование строки во вложенную карту в Clojure - PullRequest
0 голосов
/ 03 июня 2018

У меня есть файл, содержащий такой текст, как:

1|apple|sweet
2|coffee|bitter
3|gitpush|relief

Я хочу работать с этим вводом, используя карту.В Java или Python я бы создал вложенную карту, например:

{1: {thing: apple, taste: sweet},
2: {thing: coffee, taste: bitter},
3: {thing: gitpush, taste: relief}}

Или даже список внутри карты, например:

{1: [apple, sweet],
2: [coffee, bitter],
3: [grape, sour]}

Конечная цель - получить доступ к двум последнимданные столбца эффективно, используя первый столбец в качестве ключа.Я хочу сделать это в Clojure, и я новичок в этом.До сих пор мне удалось создать список карт, используя следующий код:

(def cust_map (map (fn [[id name taste]] 
       (hash-map :id (Integer/parseInt id)
                 :name name 
                 :taste taste ))
     (map #(str/split % #"\|") (line-seq (clojure.java.io/reader path)))))

И я получаю это, но это не то, что я хочу.

({1, apple, sweet},
{2, coffee, bitter},
{3, gitpush, relief})

Это было быхорошо, если вы можете показать мне, как сделать наиболее эффективную или вложенную карту и список внутри карты в Clojure.Спасибо!

Ответы [ 3 ]

0 голосов
/ 03 июня 2018

Я согласен с @coredump, что это не является кратким, но быстрое решение для вашего кода использует список (или любую другую коллекцию) и вложенную карту:

(def cust_map (map (fn [[id name taste]] 
       (list (Integer/parseInt id)
                 (hash-map :name name 
                           :taste taste)))
     (map #(clojure.string/split % #"\|") (line-seq (clojure.java.io/reader path)))))
0 голосов
/ 03 июня 2018

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

(require '[clojure.java.io :as io :refer [reader]])

(defn lines-from [fname]
  (line-seq (io/reader fname)))

(defn nested-map [fname re keys]
  "fname : full path and filename to the input file
   re    : regular expression used to split file lines into columns
   keys  : sequence of keys for the trailing columns in each line. The first column
           of each line is assumed to be the line ID"
  (let [lines       (lines-from fname)
        line-cols   (map #(clojure.string/split % re) lines)      ; (["1" "apple" "sweet"] ["2" "coffee" "bitter"] ["3" "gitpush" "relief"])
        ids         (map #(Integer/parseInt (first %)) line-cols) ; (1 2 3)
        rest-cols   (map rest line-cols)                          ; (("apple" "sweet") ("coffee" "bitter") ("gitpush" "relief"))
        rest-maps   (map #(zipmap keys %) rest-cols)]             ; ({:thing "apple", :taste "sweet"} {:thing "coffee", :taste "bitter"} {:thing "gitpush", :taste "relief"})
    (zipmap ids rest-maps)))

(nested-map "C:/Users/whatever/q50663848.txt" #"\|" [:thing :taste])

производит

{1 {:thing "apple", :taste "sweet"}, 2 {:thing "coffee", :taste "bitter"}, 3 {:thing "gitpush", :taste "relief"}}

Я показал промежуточные результаты каждого шага в блоке let в качестве комментария, чтобы вы могли видеть, что происходит.Я также бросил в lines-from, который является моей тонкой оберткой вокруг line-seq, чтобы я не печатал постоянно BufferedReader. и StringReader..: -)

0 голосов
/ 03 июня 2018

Когда вы строите карту с hash-map, аргументы являются альтернативными ключами и значениями.Например:

(hash-map :a 0 :b 1)
=> {:b 1, :a 0}

Насколько я понимаю, вы хотите иметь уникальный ключ, целое число, которое сопоставляется с составным объектом, карта:

(hash-map 0 {:thing "apple" :taste "sweet"})

Кроме того, выне хочу вызывать map, что приведет к последовательности карт.Вы хотите создать единую хэш-карту.Попробуйте использовать reduce:

(reduce (fn [map [id name taste]]
          (merge map
                 (hash-map (Integer/parseInt id)
                           {:name name :taste taste})))
        {}
        '(("1" "b" "c")
          ("2" "d" "e")))

--- edit

Вот полная программа тестирования:

(import '(java.io BufferedReader StringReader))

(def test-input (line-seq
                 (BufferedReader.
                  (StringReader.
                   "1|John Smith|123 Here Street|456-4567
2|Sue Jones|43 Rose Court Street|345-7867
3|Fan Yuhong|165 Happy Lane|345-4533"))))

(def a-map
  (reduce
   (fn [map [id name address phone]]
     (merge map
            (hash-map (Integer/parseInt id)
                      {:name name :address address :phone phone})))
   {}
   (map #(clojure.string/split % #"\|") test-input)))

a-map
=> {1 {:name "John Smith", :address "123 Here Street", :phone "456-4567"}, 2 {:name "Sue Jones", :address "43 Rose Court Street", :phone "345-7867"}, 3 {:name "Fan Yuhong", :address "165 Happy Lane", :phone "345-4533"}}
...