4

この質問は、初心者レベルの clojure の問題に該当すると思います。基本的に、clojure マップを複数回処理し、さまざまな種類のデータを抽出するのに問題があります。

このようなマップを考えると、複数のネストされたキーに基づいてエントリをカウントしようとしています:

[
  {
    "a": "X",
    "b": "M",
    "c": 188
  },
  {
    "a": "Y",
    "b": "M",
    "c": 165
  },
  {
    "a": "Y",
    "b": "M",
    "c": 313
  },
  {
    "a": "Y",
    "b": "P",
    "c": 188
  }
]

まず、エントリを a-key 値でグループ化します。

{
  "X" : [
    {
      "b": "M",
      "c": 188
    }
  ],
  "Y" : [
    {
      "b": "M",
      "c": 165
    },
    {
      "b": "M",
      "c": 313
    },
    {
      "b": "P",
      "c": 188
    }
  ]
}

次に、b キーの値を重複として想定し、残りのキーを無視します。

{
  "X" : [
    {
      "b": "M"
    }
  ],
  "Y" : [
    {
      "b": "M"
    }
    {
      "b": "P"
    }
  ]
}

次に、単純に b キーのすべてのインスタンスを数えます。

{
  "X" : 1,
  "Y" : 2
}

モンガーを介してデータを取得しているので、次のように定義しました。

(defn db-query
  ([coll-name]
     (with-open [conn (mg/connect)]
       (doall (mc/find-maps (mg/get-db conn db-name) coll-name))))

そして障害物にぶつかる:

(defn get-sums [request]
  (->> (db-query "data")
       (group-by :a)
       (into {})
        keys))

どうすればここから続けることができますか?

4

3 に答える 3

4

これは素朴なアプローチです。もっと良い方法があると確信していますが、それを理解する必要があるかもしれません。

(into {}
  (map       

    ; f       
    (fn [ [k vs] ] ;[k `unique count`]
      [k (count (into #{} (map #(get % "b") vs)))]) 

    ; coll
    (group-by #(get % "a") DATA))) ; "a"s as keys
;user=> {"X" 1, "Y" 2}

説明:

; I am using your literal data as DATA, just removed the , and ;
(def DATA [{...

(group-by #(get % "a") DATA) ; groups by "a" as keys
; so I get a map {"X":[{},...] "Y":[{},{},{},...]}

; then I map over each [k v] pair where
; k is the map key and
; vs are the grouped maps in a vector
(fn [ [k vs] ] 
      ; here `k` is e.g. "Y" and `vs` are the maps {a _, b, _, c _}

      ; now `(map #(get % "b") vs)` gets me all the b values
      ; `into set` makes them uniqe
      ; `count` counts them
      ; finally I return a vector with the same name `k`,
      ;   but the value is the counted `b`s
      [k (count (into #{} (map #(get % "b") vs)))]) 

; at the end I just put the result `[ ["Y" 2] ["X" 1] ]` `into` a map {}
; so you get a map
于 2016-03-21T19:53:41.243 に答える
2
(def data [{"a" "X", "b" "M", "c" 188}
       {"a" "Y", "b" "M", "c" 165}
       {"a" "Y", "b" "M", "c" 313}
       {"a" "Y", "b" "P", "c" 188}])
;; Borrowing data from @leetwinski

データを定義する場合に考慮すべきことの 1 つは、文字列の代わりにキーワードをキーとして使用することです。これには、キーワードを関数として使用してマップ内のものにアクセスできるという利点があり(get my-map "a")ます(:a my-map)

「a」キーでグループ化されたデータを取得するには:

(defn by-a-key [data] 
  (group-by #(get % "a") data))

2 番目のステップは、3 番目のステップに進むために使用する必要がないため、実際にはスキップできると思います。2回目の読み取りでは、個別の「b」キーごとに1つの要素のみを保持するかどうかわかりません。保持するものを選択する方法を指定しておらず、大幅に異なるように見えるため、そうではないと思います。

(reduce-kv 
  (fn [m k v] 
    (assoc m k 
      (count (filter #(contains? % "b") v)))) 
  {} 
  (by-a-key data))

次のようにすべてを行うこともできます。

(frequencies (map #(get % "a") (filter #(contains? % "b") data)))

グループ化する前に「b」キーを含むことでフィルタリングできるため、グループ化してカウントする頻度に依存できます。

于 2016-03-21T22:31:14.873 に答える
1

あなたはそれを使用してそれを作ることができますreduce:

(def data [{"a" "X", "b" "M", "c" 188}
           {"a" "Y", "b" "M", "c" 165}
           {"a" "Y", "b" "M", "c" 313}
           {"a" "Y", "b" "P", "c" 188}])

(def processed (reduce #(update % (%2 "a") (fnil conj #{}) (%2 "b")) 
                       {} data))

;; {"X" #{"M"}, "Y" #{"M" "P"}}
;; you create a map of "a" values to a sets of "b" values in one pass
;; and then you just create a new map with counts

(reduce-kv #(assoc %1 %2 (count %3)) {} processed)

;; {"X" 1, "Y" 2}

したがって、@birdspider のソリューションと同じロジックを使用しますが、コレクションのパスは少なくなります

1つの関数で:

(defn process [data]
  (->> data
       (reduce #(update % (%2 "a") (fnil conj #{}) (%2 "b")) {})
       (reduce-kv #(assoc %1 %2 (count %3)) {})))

 user> (process data)
 ;; {"X" 1, "Y" 2}
于 2016-03-21T20:20:07.450 に答える