blog.waterlow.work

Ruby, Rails, js, etc...

【ruby】【備忘録】ユーザをいくつかのグループに分ける・カウントする

備忘録
記事のタイトルと冒頭文のテストをする。 そのために、オブジェクト(ユーザ)を4つのグループに分けたい。 分け方に対する議論は一旦置いておいて、ひとまずidで順番にグループ分けをしてみる。

User = Struct.new(:id, :email, :test_pattern)
users = 57.times.map do |i|
  User.new(i, "user#{i}", { title: i % 2, statement: (i % 4) / 2})
end
# => [
#   <struct User id=0, email="user0", test_pattern={:title=>0, :statement=>0}>,
#   <struct User id=1, email="user1", test_pattern={:title=>1, :statement=>0}>,
#   <struct User id=2, email="user2", test_pattern={:title=>0, :statement=>1}>,
#   <struct User id=3, email="user3", test_pattern={:title=>1, :statement=>1}>,
#   ...
#   <struct User id=56, email="user56", test_pattern={:title=>0, :statement=>0}>
# ]

それぞれ何パターンずつ出力したのかカウントする

test_pattern_count = users.inject([]) do |array, user|
  array[user.test_pattern[:title]] ||= []
  array[user.test_pattern[:title]][user.test_pattern[:statement]] ||= 0
  array[user.test_pattern[:title]][user.test_pattern[:statement]] += 1
  array
end

info = "パターン別カウント\n"
test_pattern_count.each_with_index do |array, i|
  array.each_with_index do |count, j|
    info << "パターン#{i}_#{j}#{count}\n"
  end
end

puts info
# =>パターン別カウント
#   パターン0_0:15
#   パターン0_1:14
#   パターン1_0:14
#   パターン1_1:14

これでとりあえず出来たけど、テストする箇所が増えたりパターンが増えたりすると、 結構描き直さないと対応できない。
ただテストのパターンはころころ変わるしテストしない期間もあるためこんなもん?

【ruby】破壊的操作がなければdupは使って良さそう

最近rubydupというメソッドを使いました。dupと言うのはオブジェクトの”コピー”を作るのですが、一般的には以下の様な懸念点が有ります。

簡単に言うと

a = ["foo", "bar", "baz"]
b = a.dup
a[0].upcase!
p a #=> ["FOO", "bar", "baz"]
p b #=> ["FOO", "bar", "baz"]

オブジェクト自体は新しいものですが、インスタンス変数(配列の要素)の参照先が同じであるためこのようなことが起こっているようです。 オブジェクトを破壊的に変更しない場合にはそのようなことは起きない。

a = ["foo", "bar", "baz"]
b = a.dup
a[0] = a[0].upcase
p a #=> ["FOO", "bar", "baz"]
p b #=> ["foo", "bar", "baz"]

なので破壊的操作が存在しないFixnumやシンボル、真偽値等は特に気にせずdupして問題なさそう。

あと、この辺確かめるためにobject_idを使っていろいろしらべていたけど、以下のFixnumのオブジェクトIdの決め方の話が面白そう。

Effective Rubyを読んで気になったこと(reduce・each_with_object・inject)

途中までですが、Effective Rubyを読んでみました。

Effective Ruby

Effective Ruby

「項目19 reduceを使ってコレクションを畳み込む方法を身に付けよう」を読んだとき、まず恥ずかしながらreduceというメソッドを知らなかったです。injectは知っていたのですが、そのエイリアスだそうです。

injectreduce)と聞くと、自分ならeach_with_objectを使うかなと思いました。細かいですがinjectだとアキュムレータを返す必要があるからです。 こんなかんじです。

[1, 2, 3, 4].inject([]) do |nums, num|
  nums << num * 2
  nums
end

each_with_objectだと副作用的に更新していってくれるのでその必要はありません。

[1, 2, 3, 4].each_with_object([]) do |num, nums|
  nums << num * 2
end

視点を変えてみる

そもそもinjectreduceには発想の違いがあるようです。(非常にわかりやすい図とともに説明されています。 Rubyist Magazine map と collect、reduce と inject ―― 名前の違いに見る発想の違い

reduceを使うということは、「このコレクションの畳み込みをします。」というのをメソッドで自己ドキュメント的に宣言できるということです。(最低現reduceの単語の意味は知っておく必要がありますが…。)それに対しeach_with_objectは「コレクションのループと同時にこのobjectを更新していきましょう」ということしか読み取れません。畳み込む場合は、多少記述が増えてもreduceを使うのがいい気がしました。

結論

畳み込みする(要素を足しあわせたり、条件に応じて捨てたりする)ときはreduce、そうでなく、例えばオブジェクトのArrayからHashに変換するみたいな場合はeach_with_objectを使うのがよさそう。injectはどうしよう><

【番外編】ニッポンのジレンマ

技術関係なしです。

ニッポンのジレンマという番組のスペシャルが年始1/1にやっていたので見ました。
どんな番組かというと、1970年生まれ以降の人が集まってあるテーマに沿って議論するというもの。真剣10代しゃべり場(死語?)のターゲット変えた版のようなものでしょうか。
放送は月一で、次は2月に放送されるようです。

議論を見ているのはすごく面白かったのですが、やはり全員がそれぞれの見地で発言をして他の人の発言を聞いても最終的に「へぇ〜」で終わっているような感じがしたのでちょっと残念でした。
また、結局今後どうすりゃいいの?という問いに対する答えが得られなかったので、やっぱり自分なりに意見を持たないと、と思いました。
収録後インタビューにそれぞれの人の意見がある程度まとまっています。

あとウォンテッドリの仲暁子さんが出ていて、転職しようと思い始めた頃とか、とりあえずRailsやるためにMac買ってみよとか思った頃を思い出しました。
Wantedly 航海日誌 — 私のようなの素人のためのHacker Way
Wantedlyの仲暁子さんのTEDでのスピーチが感動的 | No:711 |

今年も頑張ろう!

Railsのソースレビューで印象に残ったこと

link_toimage_tagを使おう。

erbやhamlでは(slimも?)link_toを使わずとも以下のようにコードを埋め込めます。

<a href="<%= url>">"><%= text %></a>
%a{ href: url}= text

しかしながらモデルのインスタンスを渡すだけでパスを作ってくれたり、config/routesのasの指定を取得したりと、いろいろ便利な機能があったり、また習慣的にもビューヘルパを使って各事が多いため基本はビューヘルパを使いましょう。

・dbへの問い合わせを減らそう

モデルが以下のように定義されているとします。

class Contact < ActiveRecord::Base
  has_many :phones
end

includesを使うと以下のように後から問い合わせるsqlを先に発行するらしい(合ってる?) 以下の例だとほとんど変わらないがContactが1万件とかあると大きく違う。

irb(main):001:0> Contact.all.each { |contact| contact.phones.each { |phone| puts phone }}
  Contact Load (0.1ms)  SELECT "contacts".* FROM "contacts"
  Phone Load (0.1ms)  SELECT "phones".* FROM "phones"  WHERE "phones"."contact_id" = ?  [["contact_id", 1]]
  Phone Load (0.1ms)  SELECT "phones".* FROM "phones"  WHERE "phones"."contact_id" = ?  [["contact_id", 2]]
  Phone Load (0.1ms)  SELECT "phones".* FROM "phones"  WHERE "phones"."contact_id" = ?  [["contact_id", 3]]
irb(main):002:0> 
irb(main):003:0> Contact.includes(:phones).each { |contact| contact.phones.each { |phone| puts phone.phone }}
  Contact Load (0.1ms)  SELECT "contacts".* FROM "contacts"
  Phone Load (0.2ms)  SELECT "phones".* FROM "phones"  WHERE "phones"."contact_id" IN (1, 2, 3)

・遅延評価を知っておこう

@users = User.allのようにモデルのインスタンスを代入すると@users.each{}のように@usersメソッドを実行するときに初めてsqlが発行される。Userが膨大にいる場合は(やらないと思うけど)先に条件を絞った上でeach等のメソッドを実行する。先にallで取ってからselectなんてことはしない。

少なくともこれらの周辺知識はしっかり勉強しておく必要がありそう。

私のよちよち.rb活用法

この記事はよちよち.rb Advent Calendar 2014 - Adventar11日目の記事です。
10日目はbonbon_0605さんによるSeleniumで明示的に要素検索の待ち時間を設定するでした。
Seleniumに対しては前々から使ってみたい気持ちと、怠け癖が均衡していたのですが、bonbon_0605さんの記事で使ってみたい気持ちが勝ち越しそうです!ありがたや。

書くこと

9月に転職して、同時に参加し始めた「よちよち.rb」について参加のいきさつと、参加して思ったこと、最近の自分のよちよち活用法(自分の中での位置づけ的な?)について書いていきます。

参加のいきさつ

よちよち.rbを知ったのは今年の4月頃だったと思います。Ruby on Rails チュートリアルをやっているということで興味があったのですが、募集のページに以下のような文言が…

大切なお願い

よちよち.rb のミートアップではグループワーク形式をとっていることから、原則として参加者全員が同じスタート地点に立った状態で当日の勉強を開始したいと考えています。
そのため、上記の項までは各自ですすめてあるという前提のもとでミートアップを実施します。ご協力をよろしくお願いします。

^^;
恐ろしくなってとりあえず見送りました笑
今思えばもっと早くに参加しておけばよかったと思います…。
そして9月に転職し、新天地で偉大なる先輩odailly_jpさんに出会い、おすすめのコミュニティを聞いた所、よちよちを紹介していただきました。
かくして恐れおののいていたよちよち.rbについに参加することになったのです…。

参加して思ったこと

まずよちよち.rbは恐ろしくない!個人的に理解しきれない所もみんなでペースを合わせて進めるし、環境構築等でこけていても相談に乗ってくれる人がたくさんいます。あとは疑問を上がった時に、みんなで考えようという空気がある。もっとごりごり進んで理解できないと置いて行かれるのかと思っていましたがそんなことはなかったです。

最近の私のよちよち活用法

このコミュニティの良さを最大限利用するため、超個人的なのですが最近は以下ののようなことを考えながら参加するようにしています。

背伸びをしていろいろ言ってみる

自信がないことでも「自分はこんなふうに理解しました・解釈しました」みたいなところはなるべく言うようにしています。そうすると他の人に突っ込まれたり、突っ込まれはしないまでも新しい理解の仕方が得られたりします。

悩むのOKの場所として

独学していると、ハマったり理解できなかったりするところが辛いので、本で理解できない所は努めてすっ飛ばすようにしています。ただ個人的には1ページずつしっかり悩みつくしつつ理解したいという気持ちもあるので、ペースがわりとゆっくりなこの勉強会で達成しています。

Railsチュートリアルを進める

当たり前ですが、ちょっとずつRailsチュートリアルを進める事ができます。一人でやっているとあまり理解せずにさくっと進めて満足してしまう。もしくはゆっくりやっていてあまりの長さに途中で挫折してしまう、といったことになりがちです。よちよちに参加している限りRailsチュートリアルが積本になることはありません。

最後に

他にも言語化出来ない恩恵がたくさんありそうですが、よちよち活用法についてまとめてみました。
書きながらですが、しっかり予習していくのも楽しいかなと思いましたので今度やってみようと思います。
みなさんがどんな気持ちで参加しているのかもぜひ知りたいですね!

明日は?

明日のよちよち.rb Advent Calendar 2014はta1kt0meさんの2014年反省会です。ta1kt0meさんが今年どんなことをしてきたのか興味がありますね!