Есть ли лучший способ написать эту сводную функцию в Clojure? - PullRequest
0 голосов
/ 24 мая 2019

Я написал функцию для "поворота" некоторых таблиц данных, но мне интересно, есть ли более простой способ добиться того же результата.

(defn pivot-ab-ba
  [data]
  (r/reduce
   (fn [result [ks c]]
     (assoc-in result ks c))
   {}
   ;; pivot {a [b1 c1 b2 c2]} => [[b1 a] c1] [[b2 a] c2]
   (mapcat (fn [[a bcseq]]
             ;; pivot [a [b c]] => [[[b a] c]]
             (mapcat (fn [[b c]] [[[b a] c]]) bcseq))
           data)))

(let [data {1 {:good [1 2] :bad [3 4]}
            2 {:good [5 6] :bad [7 8]}}]
  (pivot-ab-ba data))

; => {:good {1 [1 2], 2 [5 6]}, :bad {1 [3 4], 2 [7 8]}}

Это работает, но кажется слишком сложным.

ОБНОВЛЕНИЕ:

@ ТейлорВуд предложила решение ниже;вот этот ответ с модификацией, чтобы не передавать вводимые ключи:

(defn pivot [data]
  (reduce-kv
    (fn [acc k v]
      (reduce (fn [acc' k'] (assoc-in acc' [k' k] (k' v)))
              acc
              (keys v)))
    {}
    data))

ОБНОВЛЕНИЕ 2: Спасибо всем за ваши ответы.Поскольку существует такое разнообразие ответов, я представил результаты, чтобы получить представление о том, как они выполнялись.По общему признанию, это - единственный тест, но все еще интересный:

Benchmarks performed with (criterium.core/bench pivot-function)

# Original pivot-ab-ba
Evaluation count : 8466240 in 60 samples of 141104 calls.
Execution time mean : 7.274613 µs
Execution time std-deviation : 108.681498 ns

# @TaylorWood - pivot
Evaluation count : 39848280 in 60 samples of 664138 calls.
Execution time mean : 1.568971 µs
Execution time std-deviation : 32.567822 ns

# @AlanThompson - reorder-tree
Evaluation count : 25999260 in 60 samples of 433321 calls.
Execution time mean : 2.385929 µs
Execution time std-deviation : 33.130731 ns

# @AlanThompson reorder-tree-reduce
Evaluation count : 14507820 in 60 samples of 241797 calls.
Execution time mean : 4.249135 µs
Execution time std-deviation : 89.933197 ns

# @amalloy - pivot
Evaluation count : 12721980 in 60 samples of 212033 calls.
Execution time mean : 5.087314 µs
Execution time std-deviation : 226.242206 ns

Ответы [ 4 ]

1 голос
/ 24 мая 2019

Вот еще один способ сделать это:

(defn pivot [data ks]
  (reduce-kv
    (fn [acc k v]
      (reduce (fn [acc' k'] (assoc-in acc' [k' k] (k' v)))
              acc
              ks))
    {}
    data))

Это берет карту и ожидаемые ключи, затем уменьшает по каждой паре ключ / значение в data, затем выполняет другое внутреннее уменьшение по каждому ожидаемому ключу, извлекая их значение из карты data и связывая его в выходная карта.

(def data
  {1 {:good [1 2] :bad [3 4]}
   2 {:good [5 6] :bad [7 8]}})

user=> (pivot data [:good :bad])
{:good {1 [1 2], 2 [5 6]}, :bad {1 [3 4], 2 [7 8]}}
0 голосов
/ 08 июля 2019

Быстрый perc пивот:

(let [data {1 {:good [1 2] :bad [3 4]}
            2 {:good [5 6] :bad [7 8]}}]
  (-> data
    (#%/%{:bad  (into {} (map #%/$[(key $) (-> $ val :bad)]  %))
          :good (into {} (map #%/$[(key $) (-> $ val :good)] %))})))
0 голосов
/ 24 мая 2019

Уменьшение - это излишне низкоуровневая функция для достижения этой цели.Я предпочел бы сгенерировать последовательность карт способом, который будет самоочевидным, а затем использовать merge для их объединения.Чередование комбинации логики с производственной логикой затрудняет читателю функции, чтобы увидеть, что она делает и является ли она правильной.Вместо этого полагаясь на понятные, простые функции merge и merge-with означает, что нет никакой дополнительной сложности, которую нужно понимать заново.

(defn pivot [coll]
  (apply merge-with merge
         (for [[a m] coll
               [b x] m]
           {b {a x}})))
0 голосов
/ 24 мая 2019

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

(ns tst.demo.core
  (:use demo.core tupelo.core tupelo.test) )

(defn reorder-tree
  [data]
  (let [result (atom {})]
    (doseq [[letter gb-data] data]
      (doseq [[gb-key nums-data] gb-data]
        (swap! result assoc-in [gb-key letter] nums-data)))
    @result))

(dotest
  (let [data     {:a {:good [1 2]
                      :bad  [3 4]}
                  :b {:good [5 6]
                      :bad  [7 8]}}

        expected {:good {:a [1 2]
                         :b [5 6]}
                  :bad  {:a [3 4]
                         :b [7 8]}}]

    (is= expected (reorder-tree data))))

Обновление: ОК, яне смог устоять перед написанием reduce версии с использованием вложенного for:

(defn reorder-tree-reduce
  [data]
  (reduce
    (fn [cum-map [letter gb-key nums-data]]
      (assoc-in cum-map [gb-key letter] nums-data))
    {}
    (for [[letter gb-data] data
          [gb-key nums-data] gb-data]
      [letter gb-key nums-data])))
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...