質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.47%
Ruby on Rails

Ruby on Railsは、オープンソースのWebアプリケーションフレームワークです。「同じことを繰り返さない」というRailsの基本理念のもと、他のフレームワークより少ないコードで簡単に開発できるよう設計されています。

Q&A

解決済

1回答

1301閲覧

Railsで実装する隣接リストモデルのコードについて

nakatievent

総合スコア38

Ruby on Rails

Ruby on Railsは、オープンソースのWebアプリケーションフレームワークです。「同じことを繰り返さない」というRailsの基本理念のもと、他のフレームワークより少ないコードで簡単に開発できるよう設計されています。

0グッド

0クリップ

投稿2020/09/21 15:32

編集2020/09/25 13:28

お疲れ様です。

現在共同開発でアプリを作成することになり、自分はRails側を担当することになりました。
Railsでツリー構造を実装したいと思いキータで記事を探したところ、次のような記事が見つかりデータベースのテーブルが一緒だったので参考にすることにしました。
Railsでツリー構造(階層構造)をもったカテゴリを隣接リストモデルで実装する

ただ、実装するコードの意味が全くと言っていいほど分かりません。
一応自分なりにコードの意味を調べてみたのですが、よく分からないので意味を教えてほしいです。

2. Categoryモデルの実装

class Category < ApplicationRecord belongs_to :parent, class_name: 'Category', foreign_key: :parent_id has_many :children, class_name: 'Category', foreign_key: :parent_id *カテゴリーについて、そのカテゴリーから見て1(親)の関係であれば`belongs_to`、多(子)であれば`has_many`を指定する。 def ancestors(category = self, result = [], include_self: true, only_id: true) return result + [only_id ? category.id : category] if category.root? ancestors(category.parent, result, only_id: only_id) + (!include_self && id == category.id ? [] : [only_id ? category.id : category]) end *ancestors(祖先)メソッド。include_selfは自分自身を含むという意味?only_idは一つしかIDがない場合という意味?  もし自分自身がカテゴリーの先祖だったら、配列にIDとカテゴリー名を加える? def descendants(category = self, array = [], include_self: true, only_id: true) array << (only_id ? self.id : self) if include_self && id == category.id return array + [only_id ? category.id : category] if category.children.blank? category.children.eager_load(:children).each do |cat| array << (only_id ? cat.id : cat) descendants(cat, array) end array end *descendants(子孫)メソッド。include_selfは自分自身を含むという意味?only_idは一つしかIDがない場合という意味?  もしカテゴリー自身を含み、IDがカテゴリーIDと一緒ならば、配列に加える?  もしカテゴリーの子供が空であれば、配列とカテゴリーを返す? end

調べたのは上記のとおりで、まだまだ分からない点が複数ありますのでご教授お願いします。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

winterboum

2020/09/22 07:24

このコードはそう難しいものではないです。なのになぜ回答がつかないかというと、nakatieventさんの知識レベルによってはベストアンサー3,4個分の長い戦いになりそうで手控ているのでしょう。 再帰 という概念はご存知ですか? それがベースなのでそれがわかっていらしたら回答します。わかっていなかったらまずそれを検索するなどして理解してください。
guest

回答1

0

ベストアンサー

def ancestors(category = self, result = [], include_self: true, only_id: true) return result + [only_id ? category.id : category] if category.root? ancestors(category.parent, result, only_id: only_id) + (!include_self && id == category.id ? [] : [only_id ? category.id : category]) end

このcodeで更に再帰する場合、再帰を止める場合の条件はわかりますか?

追記
再帰を止める場合 にどういう結果が返りますか

追記
result + [only_id ? category.id : category] が返るのは正解です

only_id ? category.id : category は3項演算子というやつで、ifで書くと
if only_id category.id else category end
です。
def ancestors(category = self, result = [], include_self: true, only_id: true)
result = [] は 引数 resultが与えられたらその値を、なかったら [] をresultにいれて渡す
include_self: true は 引数 {include_self: 値} が合ったらその値を、なかったら True を得れて渡す
です。
ならどういう結果かわかる?

追記
惜しい
。。。。を[]に入れて  は合っています。
が、返すのは result + [ ] です。
引数で渡ってきた resultの配列に追加して返します。

追記
def ancestors(category = self, result = [], include_self: true, only_id: true) を最初に呼ぶときは resultは[] です。そこから rootまで行って、戻ってくるときに resultはどう変化するでしょう

追記
!include_self && id == category.idはなにか?
id は引数になしmethodの中で定義もなしでいきなり参照されています。それでエラーにならないのは2つ、このインスタンスの属性か、持っている又は継承しているmethodです。名前からしてこのインスタンス.id でしょう。このインスタンスは再帰で呼ばれたインスタンスです。
対する category.idは引数で渡ってくるインスタンスのid。引数は最初はselfですから調べようとしている元。 つまり、再帰が終わって最初に戻ってきたかどうか見てます。 つまり 最初に戻ってきたら [] まだ再帰の中だったら [category.id など] を。 include_self は名前からしても「答えに自分自身を含むか否か」です。 ただ、ここちょっと私好みではない。 def ancestors(category = self, result = [], include_self=true,,,,) としておき、再帰で呼ぶときancestors(category.parent, result,false, only_id: only_id)とすればここの判定は !include_self && id == category.id でなく!include_selfで済みます。
[category = self]というのは オブジェクトそのもの とはちがいます。
self とは いま実行しているプログラムが書かれている object自身 という意味です。

投稿2020/09/22 23:48

編集2020/09/25 14:37
winterboum

総合スコア23372

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

nakatievent

2020/09/23 00:49

return result + [only_id ? category.id : category] if category.root?「もし、カテゴリーがルート[ツリー構造のはじまりの部分]だったら、結果を返す」の部分でしょうか?
nakatievent

2020/09/23 02:06

おそらくresult + [only_id ? category.id : category]の結果が返ると予想しているのですが、具体的な中身が分かりません。 Resultが[]なので、カテゴリーidとカテゴリー名を入れて[1:○○○]っていうことでしょうか? あと、[only_id ?]の意味も分かりません。
nakatievent

2020/09/23 12:38

なるほどです(^^) つまり、if category.root?(もし、カテゴリーがルートであれば、)return result + [only_id ? category.id : category](もしonly_idがあればcategory.idを、なければcategoryを[]に入れて返す)という意味で当たってますか?
nakatievent

2020/09/23 13:06 編集

つまり、返すのは result + [category.id] もしくは result + [category]ということになり、 [[category.id]] もしくは [[category]]になるということでしょうか?
winterboum

2020/09/23 13:39 編集

それだと resultの内容が反映してません。  ここちょっと修正。 rootに来た時はresultは空ですがその後は変わります および、 配列と配列の +の意味を勉強しなおしてください
nakatievent

2020/09/23 14:49 編集

irbで少し実験したところ、 result = [] a = [1] result + a #=> [1] b = ["nakatievent"] result + b #=> [nakatievent] になりました。 最初に呼び出すresultは[]なので、[]+[category.id] か []+[category]だと、返ってくるのは[category.id]もしくは[category]だと思ったのですが違いますか?
winterboum

2020/09/23 21:10

最初はそうですね、それが正しいです
nakatievent

2020/09/24 00:59

そして、ルートじゃない場合は再帰でancestorsを呼び出すので、 ancestors(category.parent, result, only_id: only_id) + (!include_self && id == category.id ? [] : [only_id ? category.id : category]) category.parentというのはそのカテゴリーの親、resultはルートの時に代入した[category]もしくは[category.id]、only_idは値があればその値、なければonly_idを返すという感じですか?
nakatievent

2020/09/24 14:17 編集

(!include_self && id == category.id ? [] : [only_id ? category.id : category]) 上のコードについては、(include_selfでなく、かつ、idがcategory.idならば[]を、それ以外ならば only_idがあればcategory.idを、なければcategoryを返す)という意味だと思っていますが当たっていますか? そもそもなんですが、記事にはidとnameとparent_idしか出てきてないのですが、[include_self] と[only_id]がどういう役割なのか分からないのでこの辺も教えてほしいです????
winterboum

2020/09/24 20:59

当たってます。 ここでの id -- の id って何者かわかりますか?
nakatievent

2020/09/24 21:26

分からないので教えてほしいです????
nakatievent

2020/09/25 14:03 編集

なるほどです。一気にレベルが上がりましたね(笑) ちょっと難しいので色々と教えてください。 インスタンスというのはクラスから作ったオブジェクト [category1 = Category.new(id: 1, name:rails, parent_id:0)]←(例) という認識で宜しいでしょうか? [category = self]というのは、例えば上の category1 = Category.new(id: 1, name:rails, parent_id:0) オブジェクトそのものという意味でしょうか? [category.id]というのが上の例でいう[id:1]という意味でしょうか?
nakatievent

2020/09/25 14:54 編集

つまり、仮に[category1.ancestors」のように、メソッドを実行しようとするインスタンスということですか?
nakatievent

2020/09/26 13:54

ありがとうございます。 当たってるか分かりませんが、少しまとめると、インスタンス(category1 = Category.new(id: 1, name:rails, parent_id:0))を作成してancestorsメソッドを実行する際に、そのインスタンスがルートであれば [result + [only_id ? category.id : category] を返す。 その際、インスタンスにはid以外にも情報があるからonly_idは適用されず、[result + category1]の結果を返すことになる。 もしルートじゃなければ再帰でancestors(category.parent, result,false, only_id: only_id)を呼び出し、[category.parent]でancestorsを実行するインスタンスの親を呼び出して(!include_self? [] : [only_id ? category.id : category])で[ []+category.parent ]の結果を返す感じで当たってますか?
winterboum

2020/09/26 14:01

「only_idは適用されず」はちがいます。 only_idは idの配列が欲しいのか、インスタンスの配列が欲しいのか、呼ぶときに指定するものです。 「もしルートじゃなければ」の説明はわかっているのか居ないのかわからない。。。 [ ] を配列のつもりで使っているのか、説明のためにカッコでくくっているのかがわからないのが、わかりにくさに拍車。 説明のためのカッコくくりなら「 」とか『 』を使って
nakatievent

2020/09/26 15:02

失礼しました。 もしルートじゃなければ再帰で親を返したいので、 ancestors(category.parent, result,false, only_id: only_id) を呼び出し、 「category.parent」でancestorsを実行するインスタンスの親を指定して、 (!include_self? [] : [only_id ? category.id : category]) で自分自身ではなく親を返すので「 []+category.parent 」を返す感じで当たってますか?
winterboum

2020/09/26 23:44

その親もrootでなかった時の動きを説明してみて
nakatievent

2020/09/27 04:21

ルートじゃない場合は、ルートになるまでancestorsメソッドを呼び出す。 その際、親がルートになるまで[]にcategory.parentを格納していく。 親がルートになったらその配列をリターンで返す。 という流れでしょうか?
winterboum

2020/09/27 05:54

「親がルートになるまで[]にcategory.parentを格納していく」ここを精密に
nakatievent

2020/09/28 01:25 編集

仮にcategory3の親をcategory2として、category2の親がcategory1でルートだとします。 まず、category3はルートではないので再帰で呼び出したancestorsメソッドでresult+ [category2]で[category2]とします。 さらに、[category2]もルートではないのでancestorsメソッドで [category2 + category1]とします。 category1はルートなので「return result + [only_id ? category.id : category] if category.root?」に該当するので値を返すという流れですか?
winterboum

2020/09/28 02:42

ん〜〜 それ再帰の理解不足です。
nakatievent

2020/09/28 03:55

すいません。 キータの記事を見て再帰のイメージは付けたんですけど、上のコードと難しさが違いすぎて全然処理の流れが分かりません。 非常に申し訳ないのですが、処理の流れを教えてほしいです。
winterboum

2020/09/28 04:21

rootでないので ancestorsメソッドを呼びます。 戻ってきた値は 親からrootまでの情報が詰まっているので そこに自分の情報を追加して 返します
nakatievent

2020/09/29 01:42

[category1 + category2 + category3]ということでしょうか?
winterboum

2020/09/29 02:26

[category1, category2 , category3] ですね
nakatievent

2020/10/01 02:02 編集

ありがとうございます(^^) チームメンバーに相談したところ、この隣接リストモデルのコードの実装は一旦保留ということになったので、ここらへんでコードについての質問を終了したいと思います。 これまで、色々と教えていただきありがとうございました! ベストアンサーに認定しときますね☺
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.47%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問