我想知道实现以下目标的最惯用方法是什么。我刚刚开始使用Clojure,我正在努力寻找操作不依赖于传统迭代结构的数据结构的策略(for,while等)。
如果我有如下地图结构:
(def test-map {:cat1-title "Title1", :cat1-val "Value1", :cat2-title "Title2", :cat2-val "Value2"})
我想将其转换为以下结构:
{"Title1" "Value1", "Title2" "Value2"}
基本上,我想创建一个新映射,其键是* title键的值,值是相应*值键的值。
这样做最好的clojure方法是什么?
我试过的是以下(我不知道它是否会一直有效)。它基本上提取*标题值,然后用提取的* val值
拉链它们(let [titles
(vals (filter #(re-matches #".*-title"
(str (key %)))
test-map))
values
(vals (filter #(re-matches #".*-val"
(str (key %)))
test-map))]
(zipmap titles values))
这成功地提取了键和值,但我不确定是否将这些与zipmap一起压缩是最好的方法,或者是最常用的组合方式。
答案 0 :(得分:2)
对于较大的地图,Zipmap将失败,因为键/值对在地图内没有严格的排序。对于小地图,它们通常遵循您创建它们的顺序,因为小地图被创建为PersistentArrayMaps。大型地图是PersistentHashMap。观察(apply hash-map (range 100))
vs (apply hash-map (range 10))
的结果如果您有更大的地图,您的标题将不会与您的-vals对齐
不幸的是,这意味着您确实需要查找与您的标题明确匹配的val。这是一种方法:
(defn transform [m]
(into {} (for [[k v] m
:let [title (name k)]
:when (.endsWith title "-title")
:let [val-name (clojure.string/replace title #"-title$" "-val")]]
[v (m (keyword val-name))])))
对于以标题结尾的每个键,使用相同的前缀查找val,并将其全部放入地图中。
答案 1 :(得分:2)
我会像Timothy一样回答这个问题,但对于生产代码,我认为最好将一些问题分散出来并更加明确:
R version 3.2.3 (2015-12-10)
Platform: x86_64-pc-linux-gnu (64-bit)
Running under: Ubuntu 14.04.4 LTS
locale:
[1] LC_CTYPE=en_GB.UTF-8 LC_NUMERIC=C LC_TIME=en_GB.UTF-8 LC_COLLATE=en_GB.UTF-8 LC_MONETARY=en_GB.UTF-8 LC_MESSAGES=en_GB.UTF-8 LC_PAPER=en_GB.UTF-8
[8] LC_NAME=C LC_ADDRESS=C LC_TELEPHONE=C LC_MEASUREMENT=en_GB.UTF-8 LC_IDENTIFICATION=C
attached base packages:
[1] stats graphics grDevices utils datasets methods base
other attached packages:
[1] Rserve_1.8-5 big.data.table_0.3.3 data.table_1.9.7
loaded via a namespace (and not attached):
[1] RSclient_0.7-3 tools_3.2.3
当然还有一些单元测试:
(ns clj.core
(:require [clojure.string :as str] )
(:use tupelo.core)) ; it->
(defn is-title-kw [arg] (re-matches #".*-title" (name arg)))
(defn title-kw->val-kw [arg] (it-> arg
(name it)
(str/replace it #"-title" "-val")
(keyword it)))
(defn transform [map-arg]
(let [title-kws (filter is-title-kw (keys map-arg)) ]
(into {}
(for [title-kw title-kws]
(let [val-kw (title-kw->val-kw title-kw)
title-str (title-kw map-arg)
val-str (val-kw map-arg) ]
{title-str val-str} )))))
运行测试:
(ns tst.clj.core
(:use clj.core
clojure.test
tupelo.core))
(def test-map { :cat1-title "Title1", :cat1-val "Value1",
:cat2-title "Title2", :cat2-val "Value2" } )
(deftest t-is-title-kw
(is (is-title-kw :cat1-title))
(is (is-title-kw :cat2-title))
(is (not (is-title-kw :cat1-val)))
(is (not (is-title-kw :cat2-val))))
(deftest t-title-kw->val-kw
(is (= :cat1-val (title-kw->val-kw :cat1-title)))
(is (= :cat2-val (title-kw->val-kw :cat2-title))))
(deftest t-transform
(is (= (transform test-map)
{ "Title1" "Value1",
"Title2" "Value2" } )))
答案 2 :(得分:2)
我更愿意使用reduce-kv
:
(defn transform [items-map]
(reduce-kv (fn [result k v]
(if-let [[_ name] (re-find #"^:(.+)-title$" (str k))]
(assoc result v (items-map (keyword (str name "-val"))))
result))
{} items-map))
在repl中:
user> (def test-map {:cat-1-title "Title1", :cat-1-val "Value1",
:cat2-title "Title2", :cat2-val "Value2",
:cat3-title "Title3", :cat3-val "Value3"})
#'user/test-map
user> (transform test-map)
{"Title1" "Value1", "Title2" "Value2", "Title3" "Value3"}