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

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

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

Rubyはプログラミング言語のひとつで、オープンソース、オブジェクト指向のプログラミング開発に対応しています。

Q&A

解決済

2回答

757閲覧

ツリー構造の子孫を全て取得する、エレガントな方法

todayske

総合スコア36

Ruby

Rubyはプログラミング言語のひとつで、オープンソース、オブジェクト指向のプログラミング開発に対応しています。

0グッド

0クリップ

投稿2020/02/01 01:54

やりたいこと

開発中の Rails アプリで、このようなツリー構造のレコードがあり、その子孫を全て一次元の配列に格納したいと思っています。
構造

全くエレガントではない私のコード

Ruby

1@items = [] 2# .subs は、その子オブジェクトを全て取得します。 3 4@item.subs.each do |i2| 5 @items.push(i2) 6 i2.subs.each do |i3| 7 @items.push(i3) 8 i3.subs.each do |i4| 9 @items.push(i4) 10 i4.subs.each do |i5| 11 @items.push(i5) 12 end 13 end 14 end 15end 16 17@items.map(&:id) # [73, 170, 173, 175, 177, 178, 176, 174, 171, 172] 18

エレガントではないポイント
0. 階層が増えると、コードが増える。
0. ほぼ同じ処理を何度も書いている。
0. 再帰的な処理なので、while とかを使えそうなのに、上手く使えない無力さが如実に表れている。

お聞きしたいこと

同じ処理を、エレガントに記述する方法をご教示いただけないでしょうか。
何卒、よろしくお願いいたします。

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

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

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

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

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

guest

回答2

0

ベストアンサー

配下の全レコードを集めるほうが、よりシンプルで、使い勝手も良さそう。

ruby

1# インスタンスメソッドとして実現 2class Record 3 def all_records 4 [self, *subs.flat_map(&:all_records)] 5 end 6end 7 8@item.all_records.map(&:id) 9 10# 関数として実現 11def collect_records(recode) 12 [ 13 recode, 14 *recode.subs.flat_map { |sub_recode| collect_records(sub_recode) } 15 ] 16end 17 18collect_records(@item).map(&:id)

投稿2020/02/01 02:29

編集2020/02/01 08:25
taichi730

総合スコア318

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

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

taichi730

2020/02/01 07:25

rubyっぽいので、こっちの方が好み
todayske

2020/02/01 07:28

見るからにエレガントなコードで、ぜひ試してみたいのですが、理解できませんでした。(すみません・・・) こちらの、:all_nodes はどんな引数なのでしょうか?
taichi730

2020/02/01 08:15

:all_records のタイポです。 (修正しました。) 下位のレコードが持っているレコードを集めて(all_records/flat_map で実現)、1つの配列に纏めています。
taichi730

2020/02/01 08:26

関数として実装した場合も追加しました。 やっていることは、両方とも同じです。
todayske

2020/02/02 02:28 編集

ご修正ありがとうございます! (自分で気付くべきでした、大変失礼しました) 驚くほど美しくなって感動しております。 エレガントなコードに、重ねまして感謝です。ありがとうございます。
guest

0

ruby

1def scan_tree(node, &block) 2 block.call(node) 3 node.subs.each do |sub_node| 4 scan_tree(sub_node, &block) 5 end 6end 7 8@id = [] 9scan_tree(@item) do |node| 10 id << node.id if node.respond_to?(:id) 11end

こんな感じですかね?
元データ (@item) がないので、動作確認はしていませんが。

投稿2020/02/01 02:14

taichi730

総合スコア318

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

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

todayske

2020/02/01 07:23

エ、エレガントだ・・・。 ほぼいただいたコードで動作確認できました! ------------------------- def scan_tree(node, &block) block.call(node) node.subs.each do |sub_node| scan_tree(sub_node, &block) end end @items = [] scan_tree(@item) do |node| @items << node end -------------------------
todayske

2020/02/01 07:23

ありがとうございます!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問