代码之家  ›  专栏  ›  技术社区  ›  smaug

在clojure中将字符串转换为嵌套映射

  •  1
  • smaug  · 技术社区  · 6 年前

    我有一个文件包含一些文本,比如:

    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中最有效地执行或同时执行嵌套映射和list inside映射,那就太好了。谢谢!

    3 回复  |  直到 6 年前
        1
  •  1
  •   coredump    6 年前

    当你用 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")))
    

    ---编辑

    以下是完整的测试程序:

    (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"}}
    
        2
  •  1
  •   manandearth    6 年前

    我同意@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)))))
    
        3
  •  1
  •   Bob Jarvis - Слава Україні    6 年前

    对于我来说,这可能是一个有点天真的观点,因为我并没有完全体验过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. 总是。-)