1:多のテーブルのそれぞれの結果をマージしてネストされたJSONにするのをRubyとClojureで関数を書いて比べてみた。

先に先日書いた
[JOINのあるSQL結果をJSONにしたかったけれどRubyとPythonでサッとわからなかったのでClojureでやってみた。](https://qiita.com/isamuJazz/items/e6c14a813558e0d51254)
でClojureでやっていたのですが勉強中のRubyでもやってみました。
免責事項的でずるいかもですが、私は業務ではどちらの言語も使ったことがないのでもっといい書き方があるかもしれません。比べる、という観点のネタ記事になります。


## そもそもタイトルにあるJSONとはどんなものか

以下の関係の２つのTABLE、```staff```と```department```から

### staff

<table border="1" style="border-collapse:collapse">
<tr><th>id</th><th>last_name</th><th>first_name</th><th>age</th><th>department_id</th></tr>
<tr><td>1</td><td>taro</td><td>yamada</td><td>37</td><td>1</td></tr>
<tr><td>2</td><td>ichiro</td><td>suzuki</td><td>30</td><td>1</td></tr>
<tr><td>3</td><td>hanako</td><td>hujiwara</td><td>25</td><td>2</td></tr></table>

### department

<table border="1" style="border-collapse:collapse">
<tr><th>id</th><th>name</th></tr>
<tr><td>1</td><td>人事</td></tr>
<tr><td>2</td><td>営業</td></tr></table>



以下のようなSQLを書いて

```mysql:SQL
select * from staff s 
inner join department dep 
on s.department_id = dep.id;
```


以下のようなJSONがサッと欲しかったです。

```JSON

[
  {
    "id": 1,
    "name": "人事",
    "staff": [
      {
        "id": 1,
        "last_name": "taro",
        "first_name": "yamada",
        "age": 37,
        "department_id": 1
      },
      {
        "id": 2,
        "last_name": "ichiro",
        "first_name": "suzuki",
        "age": 30,
        "department_id": 1
      }
    ]
  },
  {
    "id": 2,
    "name": "営業",
    "staff": [
      {
        "id": 3,
        "last_name": "hanako",
        "first_name": "hujiwara",
        "age": 25,
        "department_id": 2
      }
    ]
  }
]


```

上記のようなJSONをサッとできるメソッドが見当たらなかったので（多分実装が簡単だから？）結果的に２つの結果をマージするような関数を書くことにし、はじめは勉強がてらClojureで書いていたのですがRubyも勉強していたので書いてClojureと比べました。

## Ruby

```ruby:準備

require 'pg'
require 'json'
# postgresqlで接続
connect = PG::connect(host: "localhost", user: "user", dbname: "playground")


```

```ruby:JSON出力まで

# mapにしておく
staff_map = connect.exec("select * from staff").collect{|o| o}
department_map = connect.exec("select * from department").collect{|o| o}


def to_parent_child(parents, child_name, child, foreign_key)
  # foreign_key => valueの形にしておく
  child_group_by_foreign_key = child.group_by {|c| c[foreign_key]}
  parents.each {|p|
    p.store(child_name, child_group_by_foreign_key[p["id"]])
  }
end

parent_child =
    to_parent_child(department_map, "staffs", staff_map, "department_id")
    
puts(JSON.pretty_generate(parent_child))


```

### 結果

```JSON

[
  {
    "id": "1",
    "name": "人事",
    "staffs": [
      {
        "id": "1",
        "last_name": "taro",
        "first_name": "yamada",
        "age": "37",
        "department_id": "1"
      },
      {
        "id": "2",
        "last_name": "ichiro",
        "first_name": "suzuki",
        "age": "30",
        "department_id": "1"
      }
    ]
  },
  {
    "id": "2",
    "name": "営業",
    "staffs": [
      {
        "id": "3",
        "last_name": "hanako",
        "first_name": "hujiwara",
        "age": "25",
        "department_id": "2"
      }
    ]
  }
]

```

## Clojure

```Clojure:準備
;cheshireはmapからjsonにするライブラリ
(ns json-try
    (:require [clj-postgresql.core :as pg])
    (:require [clojure.java.jdbc :as jdbc])   
    (:require [cheshire.core :refer :all ]))
    

(def playground (pg/pool
                    :host "localhost"
                    :user "user"
                    :dbname "playground"))

;jdbc/queryメソッドが少々冗長なためラップする。               
(defn q [sql]
  (jdbc/query playground sql))


```

```clojure:JSON出力まで
;Clojureはクエリの結果はそのままmap
(def staff-map (q "select * from staff"))
(def department-map (q "select * from department"))


(defn to-parent-child [parent-map child-name child-map foreign-key]
    (let [child-group-by-foreign-key (group-by foreign-key child-map)]
        (map #(assoc % child-name (child-group-by-foreign-key (:id %)))
                 parent-map)))

;上記関数を使用して結果を束縛
(def parent-child
  (to-parent-child department-map :staffs staff-map :department_id))
  
(println (generate-string parent-child {:pretty true}))


```

### 結果

```JSON

[ {
  "id" : 1,
  "name" : "人事",
  "staffs" : [ {
    "id" : 1,
    "last_name" : "taro",
    "first_name" : "yamada",
    "age" : 37,
    "department_id" : 1
  }, {
    "id" : 2,
    "last_name" : "ichiro",
    "first_name" : "suzuki",
    "age" : 30,
    "department_id" : 1
  } ]
}, {
  "id" : 2,
  "name" : "営業",
  "staffs" : [ {
    "id" : 3,
    "last_name" : "hanako",
    "first_name" : "hujiwara",
    "age" : 25,
    "department_id" : 2
  } ]
} ]

```

# 比べてみて

これくらいのものなら特に大きな違いとかないですね。。
ちょっと気になったのが

- Rubyは関数の中の```each```をはじめは```map```で書いていたが関数の評価の結果は```department_map```のはじめの状態（mapの結果ではなかった）が返ってきた。eachで副作用を明示的にしたらうまくいった。
- Clojureは（当たり前だけれど)mapの結果が期待通りだった。

以上です。