37266


## 実現したいこと

D3.js のTreeView(以下画像のような）に利用するjsonを、Ruby on Railsアプリケーションのデータベースから動的に作成したい。

[![画像の説明をここに入力](https://i.stack.imgur.com/sXbLX.png)](https://i.stack.imgur.com/sXbLX.png)

具体的には、

`Item(name:string, parent: string) # ただし、parentは、ツリービューにおける親の名前を指す）`

のようなItemモデルのオブジェクトが多数@itemsとしてあり、  
ツリーのルートノードはparent: null のものであると仮定したとき、

[code]

    {
     "name": "明治",
     "種類": [
      {
       "name": "チョコ菓子シリーズ",
       "種類": [
        {
         "name": "きのこの山",
         "種類": [
          {"name": "味わいミント"},
          {"name": "黒糖きなこ"},
          {"name": "チョコバナナ"}
         ]
        },
        {
         "name": "たけのこの里",
         "種類": [
          {"name": "マカダミアクッキー"},
          {"name": "焼き栗"}
         ]
        },
        {
        "name": "すぎのこ村"
        }
       ]
      }
     ]
    }
    
[/code]

のようなjsonにしたいです。

## 考えたこと

@items_grouped_by_parent = @items.group_by{ |item| item.parent }

をし、@root = @items.find_by(parent: null)

子供ノードを集合として返すchildren
メソッド、それをhashとして保存するメソッド、childそれぞれに対してchildrenメソッドとhashとして保存するメソッドを再帰的に利用する。最後にview側で
to_json して、javascriptに渡す。

ここまで考えたのですが、hashの扱いに不慣れで、実装方法に悩んでいます。

## 参考にしているURL

[D3.js レイアウト – d3.tree.layoutでツリーを作る](http://deep-blend.com/jp/2014/05/d3-js-
layout-tree/)


CC BY-SA 3.0
2017-08-15T15:28:06.700
37224
2017-09-03T12:35:55.297
2017-08-15T15:48:38.947
76
17011
question
ruby-on-rails ruby アルゴリズム d3.js
データベースから取得したオブジェクト同士の親子関係をハッシュで表現したい


Rails側でItemsを木構造にしたい場合は、parentがstringではなくItemにリレーションを張るのが理想的な構造だと私は考えます。  
その場合で目的を達成するには、次の示すようなTreeView用のデータに整形するメソッドをモデルに組み込み、最後にJSON化すれば実現できます。

Itemモデル側：

[code]

    class Item < ApplicationRecord
      belongs_to :parent, class_name: "Item"
      has_many :children, class_name: "Item", foreign_key: "parent_id"
    
      def to_tree_view_nodes
        if self.children.any?
          {
            "name" => self.name,
            "種類" => self.children.map {|child| child.to_tree_view_nodes }
          }
        else
          { "name" => self.name }
        end
      end
    end
    
[/code]

コントローラーなどからJSON出力：

[code]

    @root = Item.find_by(parent_id: nil)
    @tree_view_json = @root.to_tree_view_nodes.to_json
    
[/code]

もしparentをstringで格納したい場合は、子ノードのItemsを取得するメソッドを作ることで同様にアプローチできます。ただし、Rails側の負荷が高くなるためお勧めしません。

別案ですが、もし全Itemsの数が少ないのであれば、それらをRails側が取得・そのままJSON出力したのちにJavaScript側で木構造に整形する手もあります。


CC BY-SA 3.0
2017-08-17T04:20:24.297
37266
2017-08-17T04:20:24.297
19925
37224
answer


js側で整形する場合の案です。

[code]

     var data = <%= raw  @items_flat_hash.to_json %>;
     var dataMap = {};
     data.forEach(function(node) {
            dataMap[node.id] = node;
      });
      var tree = [];
      data.forEach(function(node) {
        var parent = dataMap[node.parent_item_id];
        if (parent) {
          (parent.children || (parent.children = []))
            .push(node);
        } else {
          tree.push(node);
        }
      });
    
[/code]


CC BY-SA 3.0
2017-09-03T12:35:55.297
37697
2017-09-03T12:35:55.297
17011
37224
answer