Группировка и слияние по значению с помощью clojure? - PullRequest
0 голосов
/ 22 апреля 2019

У меня есть такой набор данных

{
  "data": [
    {
      "target_group_id": "1234",
      "target_group_name": "abc",
      "targets": [
        {
          "target_id": "456",
          "target_name": "john"
        }
      ]
    },
    {
      "target_group_id": "56789",
      "target_group_name": "cdes",
      "targets": [
        {
          "target_id": "0987",
          "target_name": "john"
        }
      ]
    },
    {
      "target_group_id": "1234",
      "target_group_name": "abc",
      "targets": [
        {
          "target_id": "789",
          "target_name": "doe"
        }
      ]
    }
  ]
}

, и я хочу преобразовать его путем группировки и объединения данных по идентификатору целевой группы, чтобы целевой объект в пределах идентичного target_group_id был добавлен к существующей целевой группе и изменился.корень ключа данных из «data» в «target_groups»

{
  "target_groups": [
    {
      "target_group_id": "1234",
      "target_group_name": "abc",
      "targets": [
        {
          "target_id": "456",
          "target_name": "john"
        },
        {
          "target_id": "789",
          "target_name": "doe"
        }
      ]
    },
    {
      "target_group_id": "56789",
      "target_group_name": "cdes",
      "targets": [
        {
          "target_id": "0987",
          "target_name": "john"
        }
      ]
    }
  ]
}

есть ли какой-нибудь эффективный способ сделать это с помощью clojure, так как мой исходный код использует php и использует много «if-clause» и «для каждого"?спасибо ...

Ответы [ 3 ]

0 голосов
/ 22 апреля 2019

Использование только ядра ядра (с библиотекой data.json).

Сначала соберите и разверните наши данные:

(def data (-> "grouping-and-merging.json"
              slurp
              clojure.data.json/read-str 
              (get "data")))

Когда мы обращаемся к группам целей, нам нужно объединить их. Я делал это inline, но при уменьшении это выглядит беспорядочно, так что вот вспомогательная функция:

(defn concat-targets [acc item]
  (update acc "targets" concat (item "targets")))

Тогда давайте сделаем работу!

(def output (->> data
                 (group-by #(get % "target_group_id"))
                 vals
                 (map #(reduce concat-targets %))
                 (assoc {} "target_groups")
                 clojure.data.json/write-str))

Мне повезло, что я справился с тем, что макросы потоков работают так хорошо, хотя вы заметите, что мне пришлось переключиться с предварительной обработки на пост-обработку между двумя фазами. Обычно я чувствую, что хочу что-то вроде Tupelo, которое он использовал в ответе Алана.

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

0 голосов
/ 23 апреля 2019

Другой способ выполнить преобразование:

{"target_groups" (map merge-vector (-> "data.json"
                                       slurp
                                       json/read-str
                                       (get "data")
                                       (set/index ["target_group_id" "target_group_name"])
                                       vals))}

;; =>
{"target_groups"
 ({"target_group_id" "1234",
   "target_group_name" "abc",
   "targets"
   ({"target_id" "789", "target_name" "doe"}
    {"target_id" "456", "target_name" "john"})}
  {"target_group_id" "56789",
   "target_group_name" "cdes",
   "targets" [{"target_id" "0987", "target_name" "john"}]})}

Промежуточная структура данных - это последовательность set, проиндексированная по идентификатору группы и имени группы (например, с использованием group-by).Т.е.

(-> "data.json"
    slurp
    json/read-str
    (get "data")
    (set/index ["target_group_id" "target_group_name"])
    vals)

;; =>
(#{{"target_group_id" "1234",
    "target_group_name" "abc",
    "targets" [{"target_id" "789", "target_name" "doe"}]}
   {"target_group_id" "1234",
    "target_group_name" "abc",
    "targets" [{"target_id" "456", "target_name" "john"}]}}
 #{{"target_group_id" "56789",
    "target_group_name" "cdes",
    "targets" [{"target_id" "0987", "target_name" "john"}]}})

targets (то есть vector) тогда concat вместе с merge-vector:

(def merge-vector
  (partial apply
           merge-with
           (fn [& xs] (if (every? vector? xs) (apply concat xs) (last xs)))))
0 голосов
/ 22 апреля 2019

Вот как я бы подошел к этому:

(ns tst.demo.core
  (:use demo.core tupelo.core tupelo.test)
  (:require
    [clojure.string :as str]
    [tupelo.string :as ts]
    [tupelo.core :as t]))

(def data-json
"{ 'data': [
    { 'target_group_id': '1234',
      'target_group_name': 'abc',
      'targets': [
        { 'target_id': '456',
          'target_name': 'john' }
      ]
    },
    { 'target_group_id': '56789',
      'target_group_name': 'cdes',
      'targets': [
        { 'target_id': '0987',
          'target_name': 'john'  }
      ]
    },
    {
      'target_group_id': '1234',
      'target_group_name': 'abc',
      'targets': [
        { 'target_id': '789',
          'target_name': 'doe'  }
      ]
    }
  ]
} " )

с преобразованием:

(dotest
  (let [data-edn (t/json->edn
                   (ts/quotes->double data-json))
        d2       (t/it-> data-edn
                   (:data it) ; unnest from :data key
                   (group-by :target_group_id it ) )
        d3       (t/forv [[tgt-id entries] d2]
                   {:tgt-group-id   tgt-id
                    :tgt-group-name (:target_group_name (first entries))
                    :targets-all    (mapv :targets entries)}) ]

и результатами / тестами:

    (is= data-edn
      {:data
       [{:target_group_id   "1234",
         :target_group_name "abc",
         :targets           [{:target_id "456", :target_name "john"}]}
        {:target_group_id   "56789",
         :target_group_name "cdes",
         :targets           [{:target_id "0987", :target_name "john"}]}
        {:target_group_id   "1234",
         :target_group_name "abc",
         :targets           [{:target_id "789", :target_name "doe"}]}]})

    (is= d2
      {"1234"
       [{:target_group_id   "1234",
         :target_group_name "abc",
         :targets           [{:target_id "456", :target_name "john"}]}
        {:target_group_id   "1234",
         :target_group_name "abc",
         :targets           [{:target_id "789", :target_name "doe"}]}],
       "56789"
       [{:target_group_id   "56789",
         :target_group_name "cdes",
         :targets           [{:target_id "0987", :target_name "john"}]}]})

    (is= d3
      [{:tgt-group-id   "1234",
        :tgt-group-name "abc",
        :targets-all    [[{:target_id "456", :target_name "john"}]
                         [{:target_id "789", :target_name "doe"}]]}
       {:tgt-group-id   "56789",
        :tgt-group-name "cdes",
        :targets-all    [[{:target_id "0987", :target_name "john"}]]}]) ))
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...