Clojure 哈希映射未显示为哈希映射

Clojure 哈希映射未显示为哈希映射,clojure,Clojure,克洛朱尔的新朋友。尝试用java背景解决以下问题。我需要将表转换为一个散列图,该散列图将产品映射到所有销售该产品的城市。所以输出应该是 {"Pencil": ("Oshawa" "Toronto") "Bread": ("Ottawa" "Oshawa" "Toronto")} (def table [ {:product "Pencil" :city "Toronto" :year "2010" :sales "2653.0

克洛朱尔的新朋友。尝试用java背景解决以下问题。我需要将表转换为一个散列图,该散列图将产品映射到所有销售该产品的城市。所以输出应该是

{"Pencil": ("Oshawa" "Toronto")
 "Bread": ("Ottawa" "Oshawa" "Toronto")}


(def table [
        {:product "Pencil"
        :city "Toronto"
        :year "2010"
        :sales "2653.00"}
        {:product "Pencil"
        :city "Oshawa"
        :year "2010"
        :sales "525.00"}
        {:product "Bread"
        :city "Toronto"
        :year "2010"
        :sales "136,264.00"}
        {:product "Bread"
        :city "Oshawa"
        :year "nil"
        :sales "242,634.00"}
        {:product "Bread"
        :city "Ottawa"
        :year "2011"
        :sales "426,164.00"}])
这就是我目前所拥有的。我将此代码写入repl

(let [product-cities {}]
(for [row table]
  (if (= (contains? product-cities (keyword (row :product))) true)
    (println "YAMON") ;;To do after. Add city to product if statement is true
    (into product-cities {(keyword (row :product)) (str (row :city))}))))
然而,结果如下:

({:Pencil "Toronto"}
 {:Pencil "Oshawa"}
 {:Bread "Toronto"}
 {:Bread "Oshawa"}
 {:Bread "Ottawa"})
我的if语句总是返回false。我看到许多散列映射周围有半圆括号。我不明白为什么它不返回一个hashmap,为什么有很多hashmap?谢谢

编辑:

问题2: 将表转换为将产品映射到销售额最高的城市的哈希映射。例如,输出应如下所示:

{"Pencil": "Toronto"
 "Bread": "Ottawa"}
我认为需要一种不同于建立价值观的战略,但以下是我的想法:

(reduce (fn [product-cities {:keys [product city sales]}]
      (update-in product-cities [product] (fnil conj []) {(keyword city) sales}))
    {}
    table)
这将产生以下输出:

{"Bread"
 [{:Toronto "136,264.00"}
  {:Oshawa "242,634.00"}
   {:Ottawa "426,164.00"}],
 "Pencil"
 [{:Toronto "2653.00"}
  {:Oshawa "525.00"}]}

然后我可以再次使用reduce函数,但只添加具有最大销售额的城市。我认为这不是最有效的方法

您需要逐一查看该表并累积(
conj
)产品下的城市:

(reduce
    (fn [acc {:keys [product city]}]
      (update acc product conj city))
    {}
    table)
;; => {"Pencil" ("Oshawa" "Toronto"), "Bread" ("Ottawa" "Oshawa" "Toronto")}
使用更新函数(
conj
,在本例中)可能有点棘手,因此这里是
update
的另一种形式:

(update acc product (fn [cities] 
                      (conj cities city)))
我不是从
{}
开始的,而是:

{"Pencil" []
 "Bread" []}
这可能更容易看出,对于表中的每个条目,
update
通过将最新的城市放在序列的末尾(这就是
conj
所做的)来更新产品密钥(“铅笔”或“面包”)。当它工作时,我只是用
{}
替换,使用
update
将插入一个新的键(如果没有)

我认为的具有生成性。它以一个序列作为输入,并在每一步生成一个新的事物-因此,你最终会得到一个新事物序列。生成时不可能更新
产品城市


reduce
对于您想要做的事情更有用,因为您得到了一个“累加器”,可以在每个步骤中稍微修改它。实际上,您每次都在通过修改传入的“累加器”来创建一个新的“累加器”,所以实际上您根本没有修改任何东西:Clojure是一种函数式语言,它的全部内容都是创建新事物

您需要逐一查看该表并累积(
conj
)产品下的城市:

(reduce
    (fn [acc {:keys [product city]}]
      (update acc product conj city))
    {}
    table)
;; => {"Pencil" ("Oshawa" "Toronto"), "Bread" ("Ottawa" "Oshawa" "Toronto")}
使用更新函数(
conj
,在本例中)可能有点棘手,因此这里是
update
的另一种形式:

(update acc product (fn [cities] 
                      (conj cities city)))
我不是从
{}
开始的,而是:

{"Pencil" []
 "Bread" []}
这可能更容易看出,对于表中的每个条目,
update
通过将最新的城市放在序列的末尾(这就是
conj
所做的)来更新产品密钥(“铅笔”或“面包”)。当它工作时,我只是用
{}
替换,使用
update
将插入一个新的键(如果没有)

我认为的具有生成性。它以一个序列作为输入,并在每一步生成一个新的事物-因此,你最终会得到一个新事物序列。生成时不可能更新
产品城市


reduce
对于您想要做的事情更有用,因为您得到了一个“累加器”,可以在每个步骤中稍微修改它。实际上,您每次都在通过修改传入的“累加器”来创建一个新的“累加器”,所以实际上您根本没有修改任何东西:Clojure是一种函数式语言,它的全部内容都是创建新事物

你似乎对clojure的工作原理有一些误解。来自java,可能很难知道如何做事情,只是因为它太不一样了。这个小问题很好地介绍了如何建立价值,我将尝试解释解决方案的每个部分

不变性 java中的一种常见模式是定义一个保存最终结果的变量,然后在添加到该变量时循环执行某些内容

这就是你想用你的
产品城市
本地做的。当您在clojure中使用
定义一个局部变量时,它永远不会改变,因此要建立一个值,您需要另一个模式

向地图添加内容 首先,我们来看看如何“添加一些东西到地图”。在clojure中,您实际要做的是用添加的东西制作一个新地图。旧地图不变。我们有时仍将其表述为添加到地图中,但这只是“使用添加的内容制作新地图”的简写

assoc
获取一个映射、一个键和一个值,并返回一个新映射,其中的值在键处添加。如果已经存在一个值,它将被覆盖。我们希望每个键都有多个属性,所以在这种情况下这不是正确的

update
与之类似,但它需要一个映射、一个键、一个函数以及该函数的可选参数。它将使用键中已经存在的值作为第一个参数和(如果存在)提供的参数调用函数。返回的映射将函数的返回值作为键处的新值。一些例子可能会更清楚地说明这一点

;; The function - is called with the old value of :foo and the argument supplied
;; (- 10 3)
(update {:foo 10} :foo - 3) ;=> {:foo 7}
如果键上没有任何内容,则将使用
nil
作为第一个参数调用该函数。这就是
nil
的意思,没有

(update {} :foo + 5) ;=> Null pointer exception. Same as (+ nil 5)
空指针是不好的。有一个技巧可以避免它们
fnil
是一个接受函数和参数的高阶函数。它返回一个新函数,用
nil
参数替换提供的参数

;; The nil here is substituted with 0, so no NPE
((fnil + 0) nil 5) ;=> 5

;; If the arg is not nil, the zero is not used.
((fnil + 0) 5 5) ;=> 10

;; So now we can update the value at :foo regardless of whether it's already there.
(update {} :foo (fnil + 0) 5) ;=> {:foo 5}
conj
向集合中添加内容。如果该集合是一个向量,则会将其添加到末尾

(conj [] :foo) ;=> :foo

(conj [:foo] :bar) ;=> [:foo :bar]
要向地图中添加内容,我们可以将以下内容组合在一起:

(update {} "product" (fnil conj []) "city") ;=> {"product ["city"]"}

(update {"product" ["city"]} "product" (fnil conj []) "another city")
;;=> {"product" ["city" "another city"]}
建立价值观 我们需要做一些循环。然而,clojure中的
for
是一个列表理解,而不是for循环。它将返回一个th序列
(reduce (fn [product-cities {:keys [product city]}]
          (update-in product-cities [product] (fnil conj []) city))
        {}
        table)