Are you sure you want to delete the question?

If your question is resolved, you may close it.

Leaving a resolved question undeleted may help others!

We hope you find it useful!

ヘッダーとその値からなるテキストをhashにしたい(出来るだけ参照が残るローカル変数なく)

こういうテキストを

list.txt
[header1]
  abc
  def

[header2]
  xxx
  yyy

[header3]
  123
  456

rubyでこういうhashにしたい

{
  "[header1]" => ["abc", "def"],
  "[header2]" => ["xxx", "yyy"],
  "[header3]" => ["123", "456"],
}

現状

ruby-2.7.1
require "active_support/all"

key = nil # => これが嫌だ
Pathname("./list.txt").readlines.collect(&:strip).group_by do |line|
  key = line if line.start_with?("[")
  key
end.to_h do |key, values|
  [key, values[1..-1].delete_if(&:blank?)]
end
# => {"[header1]"=>["abc", "def"], "[header2]"=>["xxx", "yyy"], "[header3]"=>["123", "456"]}

現在のスコープでkeyが参照できてしまうのを無くしたい。且つ、なんかもっと良いやり方があるに違いない!

0

投稿したらいきなり思いついた、がまだ他に良いやり方があるに違いない!

Pathname("./list.txt").readlines.collect(&:strip).inject({key: nil, grouped: {}}) do |memo, line|
  memo.tap do 
    memo[:key] = line if line.start_with?("[")
    memo[:grouped][memo[:key]] ||= []
    next if memo[:key] == line or line.blank?

    memo[:grouped][memo[:key]] << line 
  end
end[:grouped]
0Like

with_objectで

Pathname("./list.txt").each_line.with_object({key: nil, grouped: {}}) do |line, memo|
  line = line.strip # => なんかヤダ

  memo.tap do 
    memo[:key] = line if line.start_with?("[")
    memo[:grouped][memo[:key]] ||= []
    next if memo[:key] == line or line.blank?

    memo[:grouped][memo[:key]] << line 
  end
end[:grouped]
# 長い...
Pathname("./list.txt").readlines.collect(&:strip).to_enum.with_object({key: nil, grouped: {}}) do |line, memo|
  memo.tap do 
    memo[:key] = line if line.start_with?("[")
    memo[:grouped][memo[:key]] ||= []
    next if memo[:key] == line or line.blank?

    memo[:grouped][memo[:key]] << line 
  end
end[:grouped]
0Like

memo をシンプルにしてみました。

Pathname("./list.txt").readlines.collect(&:strip).each_with_object({}) do |line, memo|
  memo[:key] = line and next if line.start_with?("[")
  (memo[memo[:key]] ||= []) << line if line.present?
end.except(:key)

追記:値がないキーがハッシュに残らない……

Pathname("./list.txt").readlines.collect(&:strip).each_with_object({}) do |line, memo|
  memo[memo[:key] = line] = [] and next if line.start_with?("[")
  memo[memo[:key]] << line if line.present?
end.except(:key)

これだ。

1Like

:thinking:

Pathname("./list.txt")
    .each_line
    .collect(&:strip)
    .reject(&:empty?)
    .slice_before {|s| s.start_with?("[")}
    .to_h {|k, *v| [k, v]}
2Like

Your answer might help someone💌