21
10

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

[Ruby] 2次元配列とハッシュの操作を覚えよう

Posted at

はじめに

「配列とハッシュを相互変換する場面がよくあるのに、毎回ググってしまう…」
「ネストした構造になると一気に手が止まる…」
そんな方も多いのではないでしょうか?

このシリーズでは、Rubyでよく出てくる配列やハッシュの操作を整理しながら、実践的なコーディング問題を通じて着実に身につけることを目指します。

第1章:基礎固め

問題1:2次元配列の初期化と要素アクセス

問題文
3行4列の2次元配列を作り、すべての要素を0で初期化し、特定の要素にアクセスしてみましょう。

解説
Rubyでは、2次元配列は配列の配列として表現します。たとえば、matrix[1][2]は「2行目の3列目」にアクセスするという意味です。

コード例

rows = 3
cols = 4
matrix = Array.new(rows) { Array.new(cols, 0) }

p matrix #=> [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]

puts matrix[1][2] #=> 0

ポイント

  • Array.new(rows) { Array.new(cols, 0) } を使うと各行が独立した配列になります(重要)

  • Array.new(rows, Array.new(cols, 0)) は全行が同じオブジェクト参照になるので注意

問題2:ハッシュの基本操作(追加・更新・削除)

問題文
名前と年齢を持つハッシュを作成し、データの追加・更新・削除をしてみましょう。

コード例

people = { "Alice" => 25, "Bob" => 30 }

# 追加
people["Carol"] = 28

# 更新
people["Alice"] = 26

# 削除
people.delete("Bob")

puts people  #=> {"Alice"=>26, "Carol"=>28}

ポイント

  • people["新しいキー"] = 値 で追加・更新
  • .delete(キー) で削除

問題3:2次元配列からハッシュへの変換

問題文
[["Alice", 25], ["Bob", 30]] のような配列を、{ "Alice" => 25, "Bob" => 30 } に変換しましょう

コード例

array = [["Alice", 25], ["Bob", 30]]
hash = array.to_h
p hash  #=> {"Alice"=>25, "Bob"=>30}

ポイント

  • Rubyでは to_h で「配列の配列」からハッシュへの変換が簡単にできます
  • Hash[array] でも同様の変換が可能

問題4:ネストしたハッシュから値を取り出す

問題文
次のようなネスト構造のハッシュから、メールアドレスを取り出しましょう。

user = {
  name: "John",
  contact: {
    email: "john@example.com",
    phone: "090-1234-5678"
  }
}

puts user[:contact][:email]  #=> john@example.com

ポイント

  • Rubyでは :シンボル でアクセスすることが多い。
  • .dig(:contact, :email) を使えば、途中がnilでもエラーになりにくい。

問題5:ハッシュの値による並び替え

scores = { "A"=>3, "B"=>1, "C"=>2 }

sorted = scores.sort_by { |_, v| v }
p sorted #=> [["B", 1], ["C", 2], ["A", 3]]
puts sorted.to_h  #=> {"B"=>1, "C"=>2, "A"=>3}

ポイント

  • .sort_by { |k, v| v } で値でソート 配列を返す
  • .to_h でソートされた配列をハッシュに戻せる

基礎問題6:ハッシュから2次元配列への変換

問題文
以下のハッシュを、2次元配列に変換してください。

hash = { "Alice" => 25, "Bob" => 30 }
# 期待する出力:[["Alice", 25], ["Bob", 30]] 
hash = { "Alice" => 25, "Bob" => 30 }
array = hash.to_a
p array  #=> [["Alice", 25], ["Bob", 30]]

ポイント

  • .to_a を使えば、ハッシュは自動的に [key, value] ペアの配列になります。
  • この2次元配列は each でループしても扱いやすい。

応用例(ソート付き)
値の昇順に並べてから2次元配列に変換したい場合:

sorted_array = hash.sort_by { |k, v| v }
puts sorted_array.inspect  #=> [["Alice", 25], ["Bob", 30]]

キーの降順にソートするには (sort_by)

hash = { "Alice" => 25, "Bob" => 30, "Charlie" => 22, "Dave" => 28 }
# キーの文字コードを使った降順
array = hash.to_a.sort_by { |k, _v| -k.ord } 
p array  #=> [["Dave", 28], ["Charlie", 22], ["Bob", 30], ["Alice", 25]]

キーの降順にソートするには (sort)

hash = { "Alice" => 25, "Charlie" => 22, "Bob" => 30,  "Dave" => 28 }
array = hash.to_a.sort { |a, b| b[0] <=> a[0] }
p array  #=> [["Dave", 28], ["Charlie", 22], ["Bob", 30], ["Alice", 25]]

値の降順にソートするには (sort)

hash = { "Alice" => 25, "Charlie" => 22, "Bob" => 30,  "Dave" => 28 }
array = hash.to_a.sort { |a, b| b[1] <=> a[1] }
p array  #=> [["Bob", 30], ["Dave", 28], ["Alice", 25], ["Charlie", 22]]

第2章:実技演習

問題1:表形式のデータ整形(2次元配列 → 条件付き出力)

問題文
以下のような生徒データがある。
それぞれ「名前・点数・学年」が格納された2次元配列から、80点以上かつ3年生の生徒だけを出力してください。

students = [
  ["Alice", 85, 3],
  ["Bob", 78, 3],
  ["Carol", 92, 2],
  ["Dave", 88, 3]
]

期待する出力:

Alice
Dave

コード例

students = [
  ["Alice", 85, 3],
  ["Bob", 78, 3],
  ["Carol", 92, 2],
  ["Dave", 88, 3]
]

students.each do |name, score, grade|
  puts name if score >= 80 && grade == 3
end

問題2:単語出現カウント(ハッシュで集計)

問題文
与えられた単語のリストから、それぞれの単語が何回出現したかをカウントしてください。

words = ["dog", "cat", "dog", "bird", "cat", "dog"]

期待する出力:

dog 3
cat 2
bird 1

コード例

words = ["dog", "cat", "dog", "bird", "cat", "dog"]

counter = Hash.new(0) # {}

words.each do |word|
  counter[word] += 1
end

# p counter # {"dog"=>3, "cat"=>2, "bird"=>1}

counter.each do |word, count|
  puts "#{word} #{count}"
end

問題3:2次元配列の転置(行と列を入れ替える)

問題文
次のような行列を、**転置(行と列を反転)**した配列として出力してください。

matrix = [
  [1, 2, 3],
  [4, 5, 6]
]

期待する出力:

[
  [1, 4],
  [2, 5],
  [3, 6]
]

コード例

matrix = [
  [1, 2, 3],
  [4, 5, 6]
]

transposed = matrix.transpose
puts transposed.inspect #=> [[1, 4], [2, 5], [3, 6]]

問題4:条件フィルター付き検索(複合構造の操作)

問題文
次のような商品一覧から、「カテゴリが 'book' かつ 価格が1000円以下」の商品の名前を出力してください。

items = [
  { name: "Ruby Book", category: "book", price: 980 },
  { name: "T-Shirt", category: "clothes", price: 1500 },
  { name: "JS Guide", category: "book", price: 1200 },
  { name: "Notebook", category: "book", price: 700 }
]

期待する出力:

Ruby Book
Notebook

コード例

items = [
  { name: "Ruby Book", category: "book", price: 980 },
  { name: "T-Shirt", category: "clothes", price: 1500 },
  { name: "JS Guide", category: "book", price: 1200 },
  { name: "Notebook", category: "book", price: 700 }
]

items.each do |item|
  if item[:category] == "book" && item[:price] <= 1000
    puts item[:name]
  end
end

問題5:スコアランキング作成(ハッシュ+ソート)

問題文
次のようなプレイヤー名とスコアのハッシュから、スコアの降順でランキングを出力してください。

scores = {
  "Alice" => 300,
  "Bob" => 500,
  "Carol" => 400
}

期待する出力:

1: Bob - 500
2: Carol - 400
3: Alice - 300

コード例

scores = {
  "Alice" => 300,
  "Bob" => 500,
  "Carol" => 400
}

sorted_scores = scores.sort { |a, b| b[1] <=> a[1] }

sorted_scores.each.with_index(1) do |(name, score), rank|
  puts "#{rank}位: #{name} - #{score}点"
end

第3章:発展編

問題1:ログデータから日付ごとのアクセス数を集計(Date型+並び替え)

問題文
次のようなログデータの配列があります。各ログはユーザーがアクセスした日時を文字列として持っています。
Dateドキュメント
https://docs.ruby-lang.org/ja/latest/class/Date.html

logs = [
  { user: "alice", accessed_at: "2025-04-01 10:32" },
  { user: "bob", accessed_at: "2025-04-01 12:15" },
  { user: "carol", accessed_at: "2025-04-02 09:00" },
  { user: "alice", accessed_at: "2025-04-01 18:50" },
  { user: "bob", accessed_at: "2025-04-02 20:15" }
]

やること:

  • accessed_at を Date 型に変換(※日付だけ取り出す)
  • 日付ごとのアクセス数をカウント
  • 日付の昇順に並び替えて出力

期待する出力:

2025-04-01: 3
2025-04-02: 2

コード例

require "date"

logs = [
  { user: "alice", accessed_at: "2025-04-01 10:32" },
  { user: "bob", accessed_at: "2025-04-01 12:15" },
  { user: "carol", accessed_at: "2025-04-02 09:00" },
  { user: "alice", accessed_at: "2025-04-01 18:50" },
  { user: "bob", accessed_at: "2025-04-02 20:15" }
]
# p Date.parse(logs[0][:accessed_at]) #=> #<Date: 2025-04-01 ((2460767j,0s,0n),+0s,2299161j)>
# puts Date.parse(logs[0][:accessed_at]) #=> 2025-04-01

counter = Hash.new(0)

logs.each do |log|
  date = Date.parse(log[:accessed_at])
  counter[date] += 1
end

# p counter # {#<Date: 2025-04-01 ((2460767j,0s,0n),+0s,2299161j)>=>3, #<Date: 2025-04-02 ((2460768j,0s,0n),+0s,2299161j)>=>2}

counter.sort.each do |date, count|
  puts "#{date}: #{count}件"
end

問題2:ユーザーごとの最終ログイン日時を取得

問題文
次のログ配列から、各ユーザーが最後にアクセスした日時を求めてください。
https://docs.ruby-lang.org/ja/latest/method/Time/s/parse.html

logs = [
  { user: "alice", accessed_at: "2025-04-01 10:32" },
  { user: "bob", accessed_at: "2025-04-01 12:15" },
  { user: "alice", accessed_at: "2025-04-03 14:00" },
  { user: "bob", accessed_at: "2025-04-02 09:45" }
]

期待する出力:

alice: 2025-04-03 14:00
bob: 2025-04-02 09:45

コード例

require 'time'

logs = [
  { user: "alice", accessed_at: "2025-04-01 10:32" },
  { user: "bob", accessed_at: "2025-04-01 12:15" },
  { user: "alice", accessed_at: "2025-04-03 14:00" },
  { user: "bob", accessed_at: "2025-04-02 09:45" }
]

latest = {}

logs.each do |log|
  time = Time.parse(log[:accessed_at]) #=> 例1: 2025-04-01 10:32:00 +0900
  user = log[:user] #=> "alice"
  # そのユーザーのアクセス時刻が今までより新しければ更新する
  latest[user] = time if latest[user].nil? || latest[user] < time
end

latest.each do |user, time|
  puts "#{user}: #{time.strftime('%Y-%m-%d %H:%M')}"
end

問題3:カテゴリ別の売上合計を集計

問題文
以下のような売上データから、カテゴリごとの売上合計を算出してください。
https://docs.ruby-lang.org/ja/latest/method/Hash/i/each.html

sales = [
  { name: "Book A", category: "book", price: 1200 },
  { name: "Pen", category: "stationery", price: 150 },
  { name: "Book B", category: "book", price: 1500 },
  { name: "Eraser", category: "stationery", price: 100 }
]

期待する出力:

book: 2700円
stationery: 250円

コード例

sales = [
  { name: "Book A", category: "book", price: 1200 },
  { name: "Pen", category: "stationery", price: 150 },
  { name: "Book B", category: "book", price: 1500 },
  { name: "Eraser", category: "stationery", price: 100 }
]

totals = Hash.new(0) #=> {}

sales.each do |item|
  totals[item[:category]] += item[:price]
end

# p totals #=> {"book"=>2700, "stationery"=>250}

totals.each do |category, price|
  puts "#{category}: #{price}円"
end

問題4:1日ごとのユニークユーザー数を数える

問題文
ログデータから、各日付ごとにアクセスしたユニークユーザー数を数えてください。

logs = [
  { user: "alice", accessed_at: "2025-04-01 10:00" },
  { user: "bob", accessed_at: "2025-04-01 11:00" },
  { user: "alice", accessed_at: "2025-04-01 20:00" },
  { user: "bob", accessed_at: "2025-04-02 10:00" },
  { user: "carol", accessed_at: "2025-04-02 12:00" }
]

期待する出力:

2025-04-01: 2人
2025-04-02: 2人

コード例

require 'date'
require 'set'

logs = [
  { user: "alice", accessed_at: "2025-04-01 10:00" },
  { user: "bob", accessed_at: "2025-04-01 11:00" },
  { user: "alice", accessed_at: "2025-04-01 20:00" },
  { user: "bob", accessed_at: "2025-04-02 10:00" },
  { user: "carol", accessed_at: "2025-04-02 12:00" }
]

# 初めてのキーにアクセスした時に、自動で初期値として Set.new(空集合)を入れるという動作になるらしい
# 重複した要素は存在しないようにするためにSet(集合)クラスを使う。
daily_users = Hash.new { |h, k| h[k] = Set.new }
# p daily_users #=> {}

logs.each do |log|
  date = Date.parse(log[:accessed_at])
  daily_users[date] << log[:user]
end

daily_users.each do |date, users|
  puts "#{date}: #{users.size}人"
end

問題5:月別の投稿数をカウントし、月の昇順で表示

問題文
ブログ投稿データから、月ごとの投稿数をカウントして、古い順に表示してください。

posts = [
  { title: "Post 1", published_at: "2025-01-15" },
  { title: "Post 2", published_at: "2025-01-20" },
  { title: "Post 3", published_at: "2025-02-01" },
  { title: "Post 4", published_at: "2025-02-25" },
  { title: "Post 5", published_at: "2025-03-05" }
]

期待する出力:

2025-01: 2
2025-02: 2
2025-03: 1

コード例

require "date"

posts = [
  { title: "Post 1", published_at: "2025-01-15" },
  { title: "Post 2", published_at: "2025-01-20" },
  { title: "Post 3", published_at: "2025-02-01" },
  { title: "Post 4", published_at: "2025-02-25" },
  { title: "Post 5", published_at: "2025-03-05" }
]

monthly = Hash.new(0)

posts.each do |post|
  date = Date.parse(post[:published_at])
  # 年-月までに絞った上でkeyにする
  key = date.strftime("%Y-%m")
  monthly[key] += 1
end

monthly.sort.each do |month, count|
  puts "#{month}: #{count}件"
end

問題6:同一ユーザーの1日あたりのアクセス数を集計(2次元ハッシュ)

問題文
以下のログデータから、ユーザーごと・日付ごとのアクセス数を数えてください。

logs = [
  { user: "alice", accessed_at: "2025-04-01 10:32" },
  { user: "alice", accessed_at: "2025-04-01 12:10" },
  { user: "bob",   accessed_at: "2025-04-01 14:00" },
  { user: "alice", accessed_at: "2025-04-02 09:00" },
  { user: "bob",   accessed_at: "2025-04-02 10:00" }
]

期待する出力:

alice 2025-04-01: 2件
alice 2025-04-02: 1件
bob 2025-04-01: 1件
bob 2025-04-02: 1件

コード例

require "date"

logs = [
  { user: "alice", accessed_at: "2025-04-01 10:32" },
  { user: "alice", accessed_at: "2025-04-01 12:10" },
  { user: "bob",   accessed_at: "2025-04-01 14:00" },
  { user: "alice", accessed_at: "2025-04-02 09:00" },
  { user: "bob",   accessed_at: "2025-04-02 10:00" }
]

counter = Hash.new { |h, k| h[k] = Hash.new(0) }

logs.each do |log|
  user = log[:user]
  date = Date.parse(log[:accessed_at])
  counter[user][date] += 1
end

# p counter
# {"alice"=>{#<Date: 2025-04-01 ((2460767j,0s,0n),+0s,2299161j)>=>2, #<Date: 2025-04-02 ((2460768j,0s,0n),+0s,2299161j)>=>1},
# "bob"=>{#<Date: 2025-04-01 ((2460767j,0s,0n),+0s,2299161j)>=>1, #<Date: 2025-04-02 ((2460768j,0s,0n),+0s,2299161j)>=>1}}


counter.each do |user, date_map|
  date_map.each do |date, count|
    puts "#{user} #{date}: #{count}件"
  end
end

一部解説 簡単な例

# 二重ハッシュ(二段構えのHash)」を作っています
counter = Hash.new { |h, k| h[k] = Hash.new(0) }

counter["alice"][:2025_04_01] += 1

このとき、counter["alice"] はまだ存在していません。

counter["alice"] にアクセスしようとすると:

  • Ruby は alice というキーがないことに気づく
  • すると、Hash.new { |h, k| h[k] = Hash.new(0) } のブロックが呼ばれる!
  • h は今の counter 自体
  • k はアクセスしようとしたキー "alice"

ブロックの中身はこう:

h[k] = Hash.new(0)

つまり:

counter["alice"] = Hash.new(0)

これで "alice" に対して空のサブハッシュが入り、そのサブハッシュは Hash.new(0) なので、さらに未定義のキーに対してもデフォルトで 0 が返る。

最終的な構造イメージ:

counter = {
  "alice" => {
    # この中も Hash.new(0) になってる
    Date.new(2025, 4, 1) => 3,
    Date.new(2025, 4, 2) => 1
  },
  "bob" => {
    Date.new(2025, 4, 1) => 2
  }
}

問題6:曜日別のアクセス数カウント(Date#wday を使う)

問題文
ログデータから、曜日ごとのアクセス数をカウントしてください。曜日は日本語表記(月〜日)で出力。

前提ログ

logs = [
  { user: "alice", accessed_at: "2025-04-01" }, # 火
  { user: "bob",   accessed_at: "2025-04-01" },
  { user: "carol", accessed_at: "2025-04-02" }, # 水
  { user: "alice", accessed_at: "2025-04-06" }  # 日
]

期待する出力:

火: 2件
水: 1件
日: 1件

コード例

require "date"

logs = [
  { user: "alice", accessed_at: "2025-04-01" }, # 火
  { user: "bob",   accessed_at: "2025-04-01" },
  { user: "carol", accessed_at: "2025-04-02" }, # 水
  { user: "alice", accessed_at: "2025-04-06" }  # 日
]

WEEKDAYS_JP = %w[日 月 火 水 木 金 土]
counter = Hash.new(0)

logs.each do |log|
  date = Date.parse(log[:accessed_at])
  weekday = WEEKDAYS_JP[date.wday]
  counter[weekday] += 1
end

# p counter #=> {"火"=>2, "水"=>1, "日"=>1}

counter.each do |day, count|
  puts "#{day}: #{count}件"
end

問題7:時間帯(午前/午後/深夜)による分類カウント

問題文
次のログデータから、アクセスが「午前・午後・深夜」のどれに分類されるかをカウントしてください。

image.png

logs = [
  { user: "alice", accessed_at: "2025-04-01 05:00" }, # 深夜
  { user: "bob",   accessed_at: "2025-04-01 09:30" }, # 午前
  { user: "carol", accessed_at: "2025-04-01 14:45" }, # 午後
  { user: "alice", accessed_at: "2025-04-01 00:15" }, # 深夜
  { user: "bob",   accessed_at: "2025-04-01 11:59" }  # 午前
]

期待する出力:

深夜: 2件
午前: 2件
午後: 1件

コード例

require "time"

logs = [
  { user: "alice", accessed_at: "2025-04-01 05:00" }, # 深夜
  { user: "bob",   accessed_at: "2025-04-01 09:30" }, # 午前
  { user: "carol", accessed_at: "2025-04-01 14:45" }, # 午後
  { user: "alice", accessed_at: "2025-04-01 00:15" }, # 深夜
  { user: "bob",   accessed_at: "2025-04-01 11:59" }  # 午前
]

zones = Hash.new(0)

logs.each do |log|
  hour = Time.parse(log[:accessed_at]).hour

  if hour >= 0 && hour <= 5
    zones["深夜"] += 1
  elsif hour >= 6 && hour <= 11
    zones["午前"] += 1
  else
    zones["午後"] += 1
  end
end

zones.each do |zone, count|
  puts "#{zone}: #{count}件"
end

第4章:発展編(ロジック重視・条件分岐系)

問題1:マス目の最頻値とその出現回数を求めよ

問題文*
以下のような2次元配列が与えられる。全体で最も多く登場する値と、その出現回数を出力せよ。
複数ある場合は値の小さい方を選ぶ。

matrix = [
  [1, 2, 2],
  [3, 1, 2],
  [4, 2, 1],
  [1, 4, 3]
]

期待される出力:

1 4

解答コード

matrix = [
  [1, 2, 2],
  [3, 1, 2],
  [4, 2, 1],
  [1, 4, 3]
]

counts = Hash.new(0)

matrix.each do |row|
  row.each do |val|
    counts[val] += 1
  end
end

max_count = counts.values.max

most_frequent = counts.select { |_, v| v == max_count }.keys.min

puts "#{most_frequent} #{max_count}"

問題2:2次元配列の「山」(周囲より高いセル)の個数を求めよ

問題文*
上下左右の4方向すべてより値が大きいセルの数を数えてください。

grid = [
  [1, 3, 2],
  [5, 4, 6],
  [3, 2, 1]
]

期待される出力:

2

(セル(1,0)の5と、(1,2)の6が「山」)

解答コード

# 入力となる2次元配列(グリッド)
grid = [
  [1, 3, 2],
  [5, 4, 6],
  [3, 2, 1]
]

# グリッドのサイズ(行数と列数)を取得
rows = grid.size         # 行数(縦の数)→ 3
cols = grid[0].size      # 列数(横の数)→ 3

# 「山」の個数をカウントするための変数
count = 0

# 上下左右への方向を表すベクトル(行・列の変化量)
# [dx, dy] -> dxは行の変化、dyは列の変化
dirs = [
  [-1, 0],  # 上(1つ上の行)
  [1, 0],   # 下(1つ下の行)
  [0, -1],  # 左(1つ左の列)
  [0, 1]    # 右(1つ右の列)
]

# グリッドのすべてのセルを調べる
rows.times do |i|        # i は行のインデックス(縦方向)
  cols.times do |j|      # j は列のインデックス(横方向)
    current = grid[i][j] # 現在注目しているセルの値

    # 上下左右のセルすべてと比較して、全てより大きければ「山」
    is_peak = dirs.all? do |dx, dy|
      ni = i + dx # 隣接セルの行番号(dx分だけ移動) 上下
      nj = j + dy # 隣接セルの行番号(dy分だけ移動) 左右

      # 配列の外にはみ出る場合は比較せずスキップ(trueで無視)
      # 今のループをスキップして、次のループに進む
      next true if ni < 0 || nj < 0 || ni >= rows || nj >= cols

      # 現在の値が隣接セルより大きいかを判定
      current > grid[ni][nj]
    end

    # すべての方向で現在のセルの方が大きければ「山」としてカウント
    count += 1 if is_peak
  end
end

# 結果(山の数)を出力
puts count

問題3:対角線上にある同じ値のペアを数える(右下方向)

問題文*
2次元配列の中で、右下方向に位置する同じ値のペアの数を数えてください。

matrix = [
  [1, 2, 3],
  [4, 1, 6],
  [7, 8, 1]
]

期待される出力:

2

((0,0)-(1,1)、(1,1)-(2,2) が「1」)

解答コード

matrix = [
  [1, 2, 3],
  [4, 1, 6],
  [7, 8, 1]
]

count = 0
size = matrix.size

(0...size-1).each do |i|
  (0...matrix[i].size-1).each do |j|
    count += 1 if matrix[i][j] == matrix[i+1][j+1]
  end
end

puts count

問題4:条件付きブロックのカウント

問題文*
次の2次元配列において、2×2のブロックをすべて調べ、
その中に 0 が2個以上含まれるブロックの個数を数えてください。

grid = [
  [1, 0, 1, 1],
  [0, 0, 1, 0],
  [1, 1, 0, 1]
]

期待される出力:

5

解答コード

grid = [
  [1, 0, 1, 1],
  [0, 0, 1, 0],
  [1, 1, 0, 1]
]

count = 0

(0..grid.size-2).each do |i|
  (0..grid[0].size-2).each do |j|
    block = [
      grid[i][j], grid[i][j+1],
      grid[i+1][j], grid[i+1][j+1]
    ]
    count += 1 if block.count(0) >= 2
  end
end

puts count

問題5:周囲がすべて1に囲まれている「安全な0」の個数を求めよ

問題文*
次の2次元配列において、上下左右にすべて1が隣接している 0 の数を数えてください。

grid = [
  [1, 1, 1, 1, 1, 1, 1],
  [1, 1, 1, 1, 1, 1, 1],
  [1, 1, 0, 1, 0, 1, 1],
  [1, 1, 1, 1, 1, 1, 1],
  [1, 1, 0, 1, 0, 1, 1],
  [1, 1, 1, 1, 1, 1, 0],
]

期待される出力:

1

解答コード

# 入力となる2次元配列。各要素は 1 または 0。
# この中から「上下左右+斜め8方向すべてが1に囲まれている0」を探す。
grid = [
  [1, 1, 1, 1, 1, 1, 1],
  [1, 1, 1, 1, 1, 1, 1],
  [1, 1, 0, 1, 0, 1, 1],
  [1, 1, 1, 1, 1, 1, 1],
  [1, 1, 0, 1, 0, 1, 1],
  [1, 1, 1, 1, 1, 1, 0],
]

# 安全な0の数を数える変数
count = 0

# 行数と列数を取得(汎用性のため)
rows = grid.size
cols = grid[0].size

# チェックする8方向の相対座標(上下左右+斜め)
directions = [
  [-1,  0],  # 上
  [1, 0],    # 下
  [0, -1],   # 左
  [0, 1],    # 右
  [-1, -1],  # 左上
  [-1, 1],   # 右上
  [1, -1],   # 左下
  [1, 1]     # 右下
]

# 外周のマスは判定対象外なので、1〜rows-2 / 1〜cols-2 の範囲でループ
(1...rows-1).each do |i|
  (1...cols-1).each do |j|
    # 対象のセルが0でなければスキップ
    next unless grid[i][j] == 0

    # 8方向すべての隣接セルの値を取得
    neighbors = directions.map do |di, dj|
      grid[i + di][j + dj]
    end

    # 隣接する8マスすべてが1なら「安全な0」としてカウント
    if neighbors.all? { |v| v == 1 }
      count += 1
    end
  end
end

# 結果を出力(安全な0の個数)
puts count

終わりに

株式会社シンシアでは、実務未経験のエンジニアの方や学生エンジニアインターンを採用し一緒に働いています。
※ シンシアにおける働き方の様子はこちら

弊社には年間100人程度の実務未経験の方に応募いただき、技術面接を実施しております。
この記事が少しでも学びになったという方は、ぜひ wantedly のストーリーもご覧いただけるととても嬉しいです!

21
10
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
21
10

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?