Ruby tally と dig

先日やった問題で、もっといいやり方を教えてもらったのでやってみる。

masuyama13.hatenablog.com

3. 配列(リスト)の重複カウント (paizaランク D 相当)

指定された配列(リスト)の定義の中で、同じ要素の数をカウントして、その数を出力してください。

入力される値
なし

"HND", "NRT", "KIX", "NGO", "NGO", "NGO", "NGO", "NGO"
を要素に持つ配列(リスト)をプログラムで定義し、使用すること。
ただし、2つ以上同じ要素が出現するのは、1種類の文字列についてだけです。

期待する出力
同じ要素の数をカウントして、その数を出力してください。

元々の自分の解答

ary = ["HND", "NRT", "KIX", "NGO", "NGO", "NGO", "NGO", "NGO"]

def find_plural(array)
  plural =
    array.group_by(&:itself).map do | k, v |
      [k, v.size]
    end.to_h.select do | k, v |
      v > 1
    end
  plural.values
end

puts find_plural(ary)
#=> 5

tally!!

前になんかそういうメソッドがあるという話を見かけた気がして探し回ってドキュメントも検索してみたが見つけられなかったやつ。教えてもらってありがたい。

ということで、やり直してみた。

tally は、レシーバの要素ごとの数を数えて Hash で返してくれるメソッド。

Enumerable#tally (Ruby 2.7.0 リファレンスマニュアル)

array = ['HND', 'NRT', 'KIX', 'NGO', 'NGO', 'NGO', 'NGO', 'NGO']
p array.tally
#=> {"HND"=>1, "NRT"=>1, "KIX"=>1, "NGO"=>5}

返ってくるハッシュから、value が 1 より大きいものを探す。

p array.tally.find { |k, v| v > 1 }
#=> ["NGO", 5]

該当するものが配列として返ってくるので、インデックスが1の要素を取り出して出力すれば OK。

解答コード

array = ['HND', 'NRT', 'KIX', 'NGO', 'NGO', 'NGO', 'NGO', 'NGO']
puts array.tally.find { |k, v| v > 1 }[1]
#=> 5

dig

今回の問題の場合はこれで正解できたが、find で該当するものがないケースだと、下のようにエラーになる。

array = ['HND', 'NRT', 'KIX', 'NGO']
puts array.tally.find { |k, v| v > 1 }[1]
#=> Main.rb:2:in `<main>': undefined method `[]' for nil:NilClass (NoMethodError)

nil[]というメソッドはない、というエラー。

p array.tally.find { |k, v| v > 1 }
#=> nil

find で該当するものがない場合はnilが返ってくるため。

dig というメソッドを使えば、エラーを回避できる。

array = ['HND', 'NRT', 'KIX', 'NGO']
puts array.tally.find { |k, v| v > 1 }&.dig(1)

Array#dig (Ruby 2.7.0 リファレンスマニュアル)

余談:5日前から使っている Scrapbox が快適すぎて、ブログから移行しようと思い始めた(ブログをやめるわけではないけど)。

Kaigi on Rails に参加

10月3日、オンライン開催された Kaigi on Rails STAY HOME edition に参加した。Kaigi on Rails は今回が第1回目。ツールは YouTube Liveで、参加者は 200人強だった。SpatialChat でのパブリックビューイングもあった。

コンセプト

Kaigi on Railsのコアコンセプトは 「初学者から上級者までが楽しめるWeb系の技術カンファレンス」 です。
Kaigi on Railsは技術カンファレンスへの参加の敷居を下げることを意図して企画されています。また、名前の通りRailsを話題の中心に据えるカンファレンスではありますが、広くWebに関すること全般(例えばフロントエンドやプロトコルなど)についてもカバーすることで参加者の知見を深め、また明日からの仕事に役立てていただければと考えています。
About - Kaigi on Rails

当日になってこのコンセプトを知ったのだが、とてもいいなと思ったので紹介。

所感

各発表について(一部)

Ruby Kaigi でも日本語で発表されていた Aaron さん。今回は View がレンダリングされるまでの流れを、コードを交えながらの説明。とても分かりやすかった。それから、Ruby についてのブログを読みたくて日本語を勉強し始めたという話があった。発表できるほどマスターしていてすごい。自分も今まで何度も挫折してきた英語の勉強を、プログラミングを始めてからは続けられているので、逃げないで頑張ろうと思った。

Railsパフォーマンス・チューニング入門」では、少し前に耳にしたAPMツールの話や、「重いクエリ三銃士」の話が勉強になった。N+1 以外にも注意すべきところがあり、やっぱりSQLの勉強も大事だなと思った。

Railsワンマン運転の手引き」は、アプリケーションが公開できる仕組みを、電車に例えていてとても分かりやすかった。その後にDNSなどの話もあったので、先にイメージができてよかった。

「継承とメタプログラミング満載なアプリケーションコードでもアクションとフィルタに悩まないためのGemを作った話」では、Gem自体の話もだが、Gemを作ることに対する考え方が勉強になった。技術があるからGemを作るのではなく、Gemを作ることで技術がついてくる。世の中にはたくさんお手本コードがあるので、とにかくコードを読んで、書くことが大事だとわかった。自分の課題を自分のコードで解決できたら気持ちよさそう。

「コードレビュー100本ノックで学んだRailsリファクタリング」は、今年エンジニアになった方のお話ということで、全部が明日役に立ちそうだった。特にコミットの粒度については、最近試行錯誤しているところだったのでよかった。

「育てるSinatra」は、自分も Rails の前に Sinatra をやったので面白かった。Sinatra でも工夫すればいろいろなことができるのだと知った。

「新ミドルウェアActionDispatch::HostAuthorizationと学ぶDNSのしくみ」は、最初難しそうな話だと思ったが、図や説明がとても分かりやすかった。DNSバインディングという攻撃を知らなかったので、勉強できてよかった。紹介されていた本をほしい物リストに登録した。

Rubyで書かれたソースコードを読む技術」と「ActiveRecordの歩み方」では、コードの読み方やメソッドの定義場所の調べ方などが勉強になった。ソースコード読みたいので読もう。デバッグなどのやり方もちゃんと勉強していきたい。

「ひみつきちを作りたい 〜「こどもれっどまいん」テーマ作りでの学び」は、Twitter で見かけたことがあったので詳しいお話が聞けてよかった。恥ずかしながら RedMine のことを名前ぐらいしか知らなかった。

「TDD with git. Long live engineering.」では、「コードレビュー100本ノックで学んだRailsリファクタリング」で聞いた話と合わせて、コミットについてよくわかった。自分で作業するときは作業の区切りでこまめにコミットすればいいが、pushするとチームのコードになるため、あとから変更理由を追跡しやすい単位でまとめておいた方がいい。fixup は知らなかったので、勉強したい。

最後はRuby Kaigi のチーフオーガナイザーもされている松田さんによる「基調講演」。Rails 歴は13年とのこと。自分も使ったことのあるGem、kaminari の作者でもある。OSS やりたいなら「パッチを書く」以外にやることはない。

まとめ

実際参加してみて、コンセプト通りのイベントになっていたと思う。初学者向けのテーマも多く、面白かった。YouTube のチャットや Twitter もにぎわっていた。SpatialChat には参加できなかったが、懇親会も盛り上がっていたようだった。

10時から18時まで全部見るのは長いかな?と思っていたが、ほぼ全部見ていた。

今年は新型コロナウイルスの影響で大変な中、このようなイベントを開催していただいてありがたい。来年もやるとのことだったので、楽しみ。来年はもっと内容がわかるようになっているはずなので、もっと楽しめるはず。

いろいろメモを取りながら聞いていたが、実際のスライドの方が分かりやすそうなので探してみた。見つけられた範囲で張っておく。

順次動画も公開される予定とのこと。

スライド

Viewがレンダリングされるまでの技術とその理解

Railsパフォーマンス・チューニング入門

FactoryBot the Right Way

Rails 6.1's ActiveModel#errors Changes [EN]

Railsワンマン運転の手引き

継承とメタプログラミング満載なアプリケーションコードでもアクションとフィルタに悩まないためのGemを作った話

Action Mailbox in Action

コードレビュー100本ノックで学んだRailsリファクタリング

育てるSinatra

ミドルウェアActionDispatch::HostAuthorizationと学ぶDNSのしくみ

Rubyで書かれたソースコードを読む技術

ActiveRecordの歩み方

Ruby 3.0におけるドキュメンテーションと端末制御の未来

ひみつきちを作りたい 〜「こどもれっどまいん」テーマ作りでの学び

快適なリモートワークを実現するために〜RailsでSSOを実現する3パターン

TDD with git. Long live engineering.

Sidekiq to Kafka 〜ストリームベースのmicroservices〜

基調講演

Reject Kaigi on Rails に参加

10月2日、オンラインで開催された Reject Kaigi on Rails に参加した。Kaigi on Rails で採択されなかった方が発表するイベント。ツールはZOOM。参加者は19人。

内容

We can Hack Ruby

Hamada.rb の S.H さんによる発表。

  • みんなで Ruby に貢献しよう
  • みんなで Ruby を Hack しよう

コントリビュートしたいけど難しそう…

Ruby に貢献する とは

  • Ruby を使うこと
    • バンバン使う
    • 欲しい機能があったら発信する
  • バグの報告
  • テストの追加&修正
    • WSL とか結構テストが落ちる
  • ドキュメント整備
    • ドキュメントがあちこちにあり、どれが合っているか判断しにくいことも
    • C のコード読むのは辛い
    • るりま(Ruby リファレンスマニュアル)に PR 投げるといい

労働力の提供

  • 短期的な貢献はあまりよくない
  • OSS への貢献は長期的なものがいい
  • なので、無理をしないこと
  • 楽しみながらやるといい

Ruby を Hack する

Ruby の大部分は C で書かれている。

例)Integer#succ

Integer#next (Ruby 2.7.0 リファレンスマニュアル)

2.7 から RubyRuby を書ける。

  • Integer#integer? Ruby で書かれている
  • Integer#zero? は一部を C で書くことで高速さをキープ

Ruby に貢献する方法はいろいろある。

だから、みんなで Ruby をよくしていこう!!

JSONリアライザは何をしているのか

Kaigi on Rails チーフオーガナイザーの大倉さんの発表。プロポーザルを出したが採択されなかったとのことで。

有名な JSONリアライザ

  • ActiveModelSerializer(AMS)
  • fast_jsonapi
  • jbuilder

なかなかいいのがない… → 作った!

Alba というシリアライザ。簡潔な DSL と高速な動作が特徴。

JSONリアライザは何をしているのか?

答え:Hash への変換だけ

JSON への変換は JSON や Oj といった gem が高速にやってくれるが、基本的なオブジェクトしか受け付けない。

そのため、ActiveRecord などのオブジェクトは Hash に変換する必要がある。

Alba のソースコード解説

GitHub - okuramasafumi/alba: Alba is the fastest JSON serializer for Ruby.

Module#included (Ruby 2.7.0 リファレンスマニュアル)

Reject Refinements

Ruby にパッチを投げたりしている osyo さんの発表。Kaigi on Rails 本編も登壇予定。

発表資料:refinements.md · GitHub

〜ライブコーディング〜

using が使えるのは、トップレベル or クラス定義直下。ダックタイピングは NG。

サンプルコードなど以下の記事に詳しく書いてある。

secret-garden.hatenablog.com

所感

こういう LT 会とかだと、自分のような初学者はついていけないことが多いが、今回は細かいコード以外というか大枠は理解できて面白かった。発表者のお三方は勉強会などで話したことがある方ばかりだったのも個人的にはうれしかった。

Ruby に貢献・Hack したい気持ちは前からあるので、Ruby Hacking Challenge in Hamada.rb にまた参加しよう。

JSONリアライザの話は、「ないなら作る」というのがプログラマのあるべき姿だなと学んだ。自分も必要なものを作ったり、誰かが作ってくれた Gem や Ruby にコントリビュートしたりできるようになりたい。

osyo さんには以前 refine について教えてもらったことがある。今回はライブコーディングもありわかりやすかった。普段使う機会は多くないが、面白い機能で奥が深そう。

Ruby で ActiveSupport の in_groups_of を実装する - Secret Garden(Instrumental)

明日は Kaigi on Rails。最近あまり Rails 触れてないけど、気合を入れて参加予定。

Kaigi on Rails

申し込み不要、上記サイトのリンクから YouTube で見ることができる。

Ruby paizaレベルアップ問題集(文字と整数の組のソート2-3)

paizaラーニング(初心者〜中級者向けのプログラミング学習サービス)の問題集をやってみる。

paiza.jp

共通ルール

入力

  • 入力値最終行の末尾に改行が1つ入ります。
  • 文字列は標準入力から渡されます。

出力

  • 最後は改行し、余計な文字、空行を含んではいけません。

5. 数字のみの出力 (paizaランク D 相当)

1行目に行数を表す整数 n、続く n 行で n 個の「文字」と「整数」の組が空白区切りで入力されます。 n 個の整数だけをそのまま順に出力してください。

入力される値
n
S_1 D_1
S_2 D_2
...
S_n D_n

期待する出力
n 個の整数を順に改行区切りで出力してください。

条件
・1 ≦ n ≦ 10,000
・-10,000 ≦ D_i ≦ 10,000 (ただし、1 ≦ i ≦ n)
・S_iは1つの半角英文字

入力例

4
S 1
F 2
E 5
Y 6

出力例

1
2
5
6

自分の解答

n = gets.to_i
ary = []
n.times do
  a, b = gets.split
  ary << b
end
puts ary

ハッシュを使うバージョンも書いてみたが、ハッシュだと順番が変わってしまうことがあるためテストをパスできなかった。順番に意味があるときは配列がよい。

n = gets.to_i
hash = {}
n.times do
  a, b = gets.split
  hash[a] = b
end
puts hash.values
# 順番が変わってしまうためNG

6. 昇順ソート出力 (paizaランク D 相当)

1行目で整数 n が与えられ、2行目で n 個の整数が与えられます。
n 個の整数を昇順に出力してください。

入力される値
n
a_1 a_2 ... a_n

期待する出力
a_1からa_nの整数を昇順に並び替えて、出力してください。

条件
1 ≦ n ≦ 100
-3,000,000 ≦ a_i ≦ 3,000,000 (ただし、1 ≦ i ≦ n)

入力例

8
90 777 8888 121 333 4 29 2

出力例

2
4
29
90
121
333
777
8888

自分の解答

n = gets
numbers = gets.split.map(&:to_i)
puts numbers.sort

問題を見るためのチケットがなくなってしまったので今日はここまで。配列は結構思い通りに操作できるので楽しい。

スキルチェックをやればチケットが手に入るのだが、一番易しいDランクの問題はほぼすべて解き終わっていて、それ以上のランクはそこそこ時間がかかるのでなかなか難しいところ。

レベルアップ問題集をやればBランクまでいけるとのことなので頑張ろう。

Ruby paizaレベルアップ問題集(文字と整数の組のソート2-2)

paizaラーニング(初心者〜中級者向けのプログラミング学習サービス)の問題集をやってみる。

paiza.jp

共通ルール

入力

  • 入力値最終行の末尾に改行が1つ入ります。
  • 文字列は標準入力から渡されます。

出力

  • 最後は改行し、余計な文字、空行を含んではいけません。

3. 配列(リスト)の重複カウント (paizaランク D 相当)

指定された配列(リスト)の定義の中で、同じ要素の数をカウントして、その数を出力してください。

入力される値
なし

"HND", "NRT", "KIX", "NGO", "NGO", "NGO", "NGO", "NGO"
を要素に持つ配列(リスト)をプログラムで定義し、使用すること。
ただし、2つ以上同じ要素が出現するのは、1種類の文字列についてだけです。

期待する出力
同じ要素の数をカウントして、その数を出力してください。

自分の解答

ary = ["HND", "NRT", "KIX", "NGO", "NGO", "NGO", "NGO", "NGO"]

def find_plural(array)
  plural =
    array.group_by(&:itself).map do | k, v |
      [k, v.size]
    end.to_h.select do | k, v |
      v > 1
    end
  plural.values
end

puts find_plural(ary)
#=> 5

group_byitselfを渡せるの知らなかった。

irb(main):001:0> ary = ["HND", "NRT", "KIX", "NGO", "NGO", "NGO", "NGO", "NGO"]
irb(main):002:0> ary.group_by(&:itself)
#=> {"HND"=>["HND"], "NRT"=>["NRT"], "KIX"=>["KIX"], "NGO"=>["NGO", "NGO", "NGO", "NGO", "NGO"]}

group_byにブロックを渡すとハッシュが返ってくる。それを map で処理すると、今度は Array(配列)が返ってくるので、to_h でハッシュに変換。

irb(main):003:0> ary.group_by(&:itself).map { |k, v| [k, v.size] }.to_h
#=> {"HND"=>1, "NRT"=>1, "KIX"=>1, "NGO"=>5}

結構難しかった。 もっとハッシュに強くなりたい〜

Enumerable#group_by (Ruby 2.7.0 リファレンスマニュアル)

4. 配列のソート (paizaランク D 相当)

指定した配列を定義し、配列の要素を昇順に並び替えて、全て出力してください。

入力される値
なし

1, 3, 5, 6, 3, 2, 5, 23, 2
を要素に持つ配列をプログラムで定義し、使用すること。

期待する出力
昇順に並び替えて、1要素ごとに改行して出力してください。

自分の解答

ary = [1, 3, 5, 6, 3, 2, 5, 23, 2]
puts ary.sort

Ruby paizaレベルアップ問題集(文字と整数の組のソート2-1)

paizaラーニング(初心者〜中級者向けのプログラミング学習サービス)の問題集をやってみる。

paiza.jp

共通ルール

入力

  • 入力値最終行の末尾に改行が1つ入ります。
  • 文字列は標準入力から渡されます。

出力

  • 最後は改行し、余計な文字、空行を含んではいけません。

1. インクリメント (paizaランク D 相当)

1つの整数nが入力されるので、その値に1足した値を出力してください。

入力される値
n

期待する出力
nに1を足し算した数値を出力してください。

自分の解答

n = gets.to_i
puts n + 1

2. 重複の判定 (paizaランク D 相当)

指定された配列(リスト)の定義の中で、同じ値が存在した場合はtrueを、そうでない場合はfalseを出力してください。

入力される値
なし

HND, NRT, KIX, NGO, NGO
を要素に持つ配列(リスト)をプログラムで定義し、使用すること。

期待する出力
配列(リスト)の要素に重複があればtrueを、重複がなかったらfalseを出力する。

自分の解答

ary = ["HND", "NRT", "KIX", "NGO", "NGO"]
puts ary.size != ary.uniq.size

最初 puts ary.size != ary.uniq.size ? true : false と書いてしまった。

Ruby では、式を書けば truefalse が返ってくるので、冗長だった。ときどきそんな風に書いてしまうけど、冗長だなと気づけるようになっただけ成長したかもしれない。

メモ取るの大事

最近 JavaScript を勉強し始めて、改めてメモの大切さを感じた。

しばらく JavaScript を書いてから Ruby を書くと、ちょっとしたところで、あれどう書くんだったっけ?となってしまうことがある。コメントとか式展開とか…。

学生の頃は、勉強したことは覚えないと意味がなかったのであまりノートを取っていなかったのだが、大人の勉強で大事なのは、未来の自分が簡単に思い出せるようなメモを書くことだと気づいた。

基本の基本や調べればすぐわかるからさすがにメモしておかなくてもいいだろう、というようなことも意外と必要だ。未来の自分は、今の自分が思っているほど賢くない。

少し前から、学んだことなどを VSCode でメモするようにした。

VSCode をマークダウンエディタとして使う - No Solution for Life

現状

note というディレクトリ(名前は適当)を作って、マークダウンファイルを作っていっている。とりあえずファイルを作って、同じカテゴリのファイルが複数になったらディレクトリを作ろうと思っている。

現状はこんな感じ。

$ ls -F
bash.md         english.md      html.md         markdown.md     note.md         ruby/           shortcuts.md    tmp.md
books/          git.md          javascript.md   news.md         random/         seminars/       sql/            vue.md

コードや参考 URL、自分が実際にやった手順など、なんでも書いていっている。

メモの書き方やディレクトリ構成など考えたいことはいろいろありつつも、完璧主義という悪い癖のせいでメモツールすら定まらない状態が何ヶ月も続いてしまったので、とりあえずやりながら改善していくようにしたい。

これを GitHub のプライベートリポジトリに push しておけばいいかなと思う(非公開ページのURLが入っているので公開は難しい)。いつか違うエディタを使うようになったとしても、ただのマークダウンファイルなので安心。

コードに限れば、til(Today I Learned)というようなリポジトリを作る人も多いらしい。今は参考 URL とかごちゃごちゃなんでも書いていっているので微妙だが、いずれやってみたい気がする。

エイリアスを設定すると便利

このディレクトリを VSCode で開くコマンドをエイリアスに設定しておくと便利!

.bashrc

alias note='code /Users/masuyama/note'

これで、note と打ってエンターキーを押すだけで VSCodeワークスペースが立ち上がる。面倒さが減ってメモを書くのも楽しくなった。

一応満足しているが、今後いいツールが見つかったら積極的に使っていきたい。Scrapbox も試しに使ってみよう。