rails:index_byメソッドで配列をkey-value型hashに変換

配列で受け取ったデータをeachで検索(リニアサーチ)するより、key-value型のhashに変換してkey指定で取得した方が、コストもかからず効率がよい。

index_by メソッドを使うと、key-value型のハッシュに変換してくれる。
ただし、このメソッドは rails(active_support)でサポートされているので、通常のrubyでは使用できないので注意。
(active_support によって Enumerable に追加されたメソッド)

なお、index_byメソッド では値を一つしか保持しないため、同じ key を持つデータが複数ある場合には後から来たデータに値が上書きされるので、keyに使用するのは、ユニークな項目であることが前提。

例えば、以下のような結果になるActiveRelation集合があったとする。

> @a = Master.find_by(status : 10)
[{key: 1, status: 10, staff_id: 8}, {key: 5, status: 10, staff_id: 1}]

> @ai = @a.index_by(&:staff_id)
[{8: {key: 1, status: 10, staff_id: 8}}, (1: {key: 5, status: 10, staff_id: 1}}]

>@as[8]
{key: 1, status: 10, staff_id: 8}}
>@as[1]
{key: 5, status: 10, staff_id: 1}

>@as[8].status
10

Rails でオブジェクトの配列を簡単にハッシュ化したい - Qiita

Railsでindex_byを使ってハッシュ形式のデータを受け取る - (゚∀゚)o彡 sasata299's blog

ruby:hash配列のkey検索

hash配列を指定keyで検索し、取得する。

mast = [{code: "101", name:"abc"},
  {code: "201", name:"cde"},
  {code: "101", name:"a"},
  {code: "102", name:"efg"},
  {code: "101", name:"abc"},
  {code: "301", name:"b"}]

select だと該当する全行を取得(Array)。

>> result = mast.select { |a| a[:name].include?('abc') }                                                                                     
=> [{:code=>"101", :name=>"abc"}, {:code=>"101", :name=>"abc"}]

>> result = mast.select { |a| a[:name].include?('a') }
=> [{:code=>"101", :name=>"abc"}, {:code=>"101", :name=>"a"}, {:code=>"101", :name=>"abc"}]

find だと最初に該当した行のみ(hash)

>> result = mast.find { |a| a[:name].include?('a') }                                                                                         
=> {:code=>"101", :name=>"abc"}

>> result = mast.find { |a| a[:name]=='abc' }
=> {:code=>"101", :name=>"abc"}
>> result = mast.find { |a| a[:name]=='b' }
=> {:code=>"301", :name=>"b"}

ruby:csv操作

CSVデータの操作サンプル。
parse すると、array になる。

>> require 'csv'
=> true
>> require 'pp'
=> false

>> s = <<EOS
id,first name,last name,age
1,taro,tanaka,20
2,jiro,suzuki,18
EOS

>> pp s
"id,first name,last name,age\n" + "1,taro,tanaka,20\n" + "2,jiro,suzuki,18\n"
=> "id,first name,last name,age\n1,taro,tanaka,20\n2,jiro,suzuki,18\n"

>> pp CSV.parse(s)
[["id", "first name", "last name", "age"],
 ["1", "taro", "tanaka", "20"],
 ["2", "jiro", "suzuki", "18"]]
=> [["id", "first name", "last name", "age"], ["1", "taro", "tanaka", "20"], ["2", "jiro", "suzuki", "18"]]

heades: true 指定してparaseすると扱いやすい。

>> pp CSV.parse(s, headers: true)
#<CSV::Table mode:col_or_row row_count:3>
=> #<CSV::Table mode:col_or_row row_count:3>

ss = CSV.parse(s, headers: true)
ss.each do |row|
  p row
end

>> ss.each do |row|
?>   p row
>> end
#<CSV::Row "id":"1" "first name":"taro" "last name":"tanaka" "age":"20">
#<CSV::Row "id":"2" "first name":"jiro" "last name":"suzuki" "age":"18">
=> #<CSV::Table mode:col_or_row row_count:3>

ss.each do |row|
  p row["age"]
end

>> ss.each do |row|
?>   p row["age"]
>> end
"20"
"18"
=> #<CSV::Table mode:col_or_row row_count:3>

その他、いろいろな操作。

sss = ss.map {|row| row['first name']}

>> sss = ss.map {|row| row['first name']}
=> ["taro", "jiro"]

ss.each.with_index do |row, i|
  p i
  p '------------------------'
  row.each do |k, v|
    p k + ' = ' + v
  end
end

>> ss.each.with_index do |row, i|
?>   p i
>>   p '------------------------'
>>   row.each do |k, v|
?>     p k + ' = ' + v
>>   end
>> end
0
"------------------------"
"id = 1"
"first name = taro"
"last name = tanaka"
"age = 20"
1
"------------------------"
"id = 2"
"first name = jiro"
"last name = suzuki"
"age = 18"
=> #<CSV::Table mode:col_or_row row_count:3>

CSV.parse (Ruby 3.1 リファレンスマニュアル)
CSV.new (Ruby 3.1 リファレンスマニュアル)

unix:コマンドメモ

よく使うコマンド。

# 環境確認
env

# システム状況確認(プロセス、CPU、メモリ等の統計情報)
# ctrl + c で中断
top           # cpu使用率順
top -a        # メモリ使用率順
top -a [PID]  # 特定プロセスを監視
top -d1       # 1秒ごとに更新
top | grep ruby

# ディスクの使用量、使用率
df

# ファイル、ディレクトリの使用量確認(kbyte)
du

# プロセス確認
ps -ef
ps -ef | grep ruby

# 文字列grepで検索
grep hoge *.txt
grep hoge work/*

git:stashで変更内容を退避、別ブランチに反映

ベースにしていたブランチが更新された時など、
rebaseせずに、変更内容を退避、再反映するには、以下の流れ。

# ブランチ構成が以下
# feature/baseブランチから、devブランチを切って作業中
#   master
#   feature/base
# * dev/task001

# 変更を退避(-u は --include-untrackedの略で、新規作成ファイルも退避)
git stash -u

# 変更がなくなっていることを確認
git status

# 退避した変更が登録されていることを確認
git stash list

# 以下、ブランチをベースに戻し、最新化して別ブランチ作成、移動
git checkout feature/base
git pull origin feature/base
git branch dev/task002
git checkout dev/task002

# 退避した変更が登録されていることを確認
# 番号を確認、直前であれば{0}となっているはず
git stash list

# 退避した変更を戻す
# stash@{0} を指定しないと、直近が戻る
git stash apply stash@{0}

# 変更が反映されていることを確認
git status

# 退避した変更を消す
# stash@{0} を指定しないと、直近が消える
git stash drop stash@{0}

rails:migrate実行

db:migrateの実行について。

# modelからgenerate (migrationも作成される)
rails g model staffs
bin/rails g model staffs #どっちでも

# table追加・変更だけのときはmigrationからgenerate
rails g migration AddTypeOfGenderStaffs
rails g migration CreateStaffHistorys

# migrateの実行
rails db:migrate

# 現在バージョンの確認
rails db:version

# 戻したい場合は、以下で1回分戻る
rails db:rollback

# migrateの実施履歴の参照 downは未適用
rails db:migrate:status

# バージョンを指定してmigrate
rails db:migrate:up VERSION=2022mmddhhmmss

migrationファイルの書き方については別途。

SQL:条件付きCOUNT

今まで知りませんでした。
条件付きのcountがこんなに簡単にとれるとは。

select
   count(*) # 全件
  ,count(staffs.type = 1 OR NULL) as byte_count
  ,count(staffs.type = 2 OR NULL) as part_count
from
  staffs;
where
  staffs.invalid = true

参考:多謝)
【SQL】COUNT関数で条件に合致する件数を取得する - Qiita
SQLでSELECT句のCOUNTをきわめる【複数の条件指定も可能】 | ジユーズ