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

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

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

## 第1章：基礎固め

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

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

**コード例**
```ruby 
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：ハッシュの基本操作（追加・更新・削除）
**問題文**
名前と年齢を持つハッシュを作成し、データの追加・更新・削除をしてみましょう。

**コード例**
```ruby 
people = { "Alice" => 25, "Bob" => 30 }

# 追加
people["Carol"] = 28

# 更新
people["Alice"] = 26

# 削除
people.delete("Bob")

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

# mergeメソッド - 別のハッシュを統合
new_people = { "Dave" => 35, "Eve" => 29 }
people.merge!(new_people)  # 破壊的メソッド（元のハッシュが変更される）
puts people  #=> {"Alice"=>26, "Carol"=>28, "Dave"=>35, "Eve"=>29}

# 非破壊的mergeの場合
combined = people.merge({ "Frank" => 40 })  # 元のハッシュはそのまま、新しいハッシュを返す
puts combined  #=> {"Alice"=>26, "Carol"=>28, "Dave"=>35, "Eve"=>29, "Frank"=>40}
puts people    #=> {"Alice"=>26, "Carol"=>28, "Dave"=>35, "Eve"=>29}

# キーの存在確認
puts people.key?("Alice")  #=> true
puts people.has_key?("Bob")  #=> false

# 値の取得（安全なアクセス）
puts people.fetch("Alice", "Not found")  #=> 26
puts people.fetch("Bob", "Not found")    #=> Not found

# すべてのキー・値を取得
puts people.keys.inspect  #=> ["Alice", "Carol", "Dave", "Eve"]
puts people.values.inspect  #=> [26, 28, 35, 29]

# キーと値のペアで繰り返し処理
people.each do |name, age|
  puts "#{name} is #{age} years old"
end

# ハッシュの変換
people_array = people.to_a  # ハッシュを配列に変換
puts people_array.inspect  #=> [["Alice", 26], ["Carol", 28], ["Dave", 35], ["Eve", 29]]

# フィルタリング
young_people = people.select { |name, age| age < 30 }
puts young_people  #=> {"Alice"=>26, "Carol"=>28, "Eve"=>29}
```
**ポイント**
- people["新しいキー"] = 値 で追加・更新
- .delete(キー) で削除

### 問題3：2次元配列からハッシュへの変換
**問題文**
[["Alice", 25], ["Bob", 30]] のような配列を、{ "Alice" => 25, "Bob" => 30 } に変換しましょう

**コード例**
```ruby 
array = [["Alice", 25], ["Bob", 30]]
hash = array.to_h
p hash  #=> {"Alice"=>25, "Bob"=>30}
```
**ポイント**
- Rubyでは to_h で「配列の配列」からハッシュへの変換が簡単にできます
- Hash[array] でも同様の変換が可能

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

```ruby 
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：ハッシュの値による並び替え
```ruby 
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次元配列に変換してください。
```ruby 
hash = { "Alice" => 25, "Bob" => 30 }
# 期待する出力：[["Alice", 25], ["Bob", 30]] 
```

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

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

**応用例（ソート付き）**
値の昇順に並べてから2次元配列に変換したい場合：
```ruby 
sorted_array = hash.sort_by { |k, v| v }
puts sorted_array.inspect  #=> [["Alice", 25], ["Bob", 30]]
```
キーの降順にソートするには (sort_by)
```ruby
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)
```ruby 
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年生の生徒だけを出力してください。

```ruby
students = [
  ["Alice", 85, 3],
  ["Bob", 78, 3],
  ["Carol", 92, 2],
  ["Dave", 88, 3]
]
```
**期待する出力：**

```
Alice
Dave
```
**コード例**
```ruby 
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：単語出現カウント（ハッシュで集計）
**問題文**
与えられた単語のリストから、それぞれの単語が何回出現したかをカウントしてください。

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

**期待する出力：**

```
dog 3
cat 2
bird 1
```

**コード例**

```ruby 
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次元配列の転置（行と列を入れ替える）
**問題文**
次のような行列を、**転置（行と列を反転）**した配列として出力してください。

```ruby
matrix = [
  [1, 2, 3],
  [4, 5, 6]
]
```
期待する出力：

```ruby
[
  [1, 4],
  [2, 5],
  [3, 6]
]
```
**コード例**

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

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

### 問題4：条件フィルター付き検索（複合構造の操作）
**問題文**
次のような商品一覧から、「カテゴリが 'book' かつ 価格が1000円以下」の商品の名前を出力してください。

```ruby
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
```

**コード例**
```ruby 
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：スコアランキング作成（ハッシュ＋ソート）
**問題文**
次のようなプレイヤー名とスコアのハッシュから、スコアの降順でランキングを出力してください。

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

**期待する出力：**

```ruby 
1位: Bob - 500点
2位: Carol - 400点
3位: Alice - 300点
```

**コード例**

```ruby 
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

```ruby
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 型に変換（※日付だけ取り出す）
- 日付ごとのアクセス数をカウント
- 日付の昇順に並び替えて出力

 **期待する出力：**

```ruby
2025-04-01: 3件
2025-04-02: 2件
```

**コード例**
```ruby 
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

```ruby
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" }
]
```

**期待する出力：**

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

**コード例**
```ruby 
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

```ruby
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円
```

**コード例**
```ruby 
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日ごとのユニークユーザー数を数える
**問題文**
ログデータから、各日付ごとにアクセスしたユニークユーザー数を数えてください。

```ruby
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人
```

**コード例**
```ruby 
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：月別の投稿数をカウントし、月の昇順で表示
**問題文**
ブログ投稿データから、月ごとの投稿数をカウントして、古い順に表示してください。

```ruby
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" }
]
```
**期待する出力：**

```ruby
2025-01: 2件
2025-02: 2件
2025-03: 1件
```

**コード例**
```ruby
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次元ハッシュ）
**問題文**
以下のログデータから、ユーザーごと・日付ごとのアクセス数を数えてください。

```ruby
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件
```

**コード例**
```ruby 
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
```

**一部解説 簡単な例**
```ruby 
# 二重ハッシュ（二段構えの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"

**ブロックの中身はこう：**
```ruby
h[k] = Hash.new(0)
```
つまり：
```ruby
counter["alice"] = Hash.new(0)
```
これで "alice" に対して空のサブハッシュが入り、そのサブハッシュは Hash.new(0) なので、さらに未定義のキーに対してもデフォルトで 0 が返る。

**最終的な構造イメージ:**
```ruby 
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 を使う）
**問題文**
ログデータから、曜日ごとのアクセス数をカウントしてください。曜日は日本語表記（月〜日）で出力。

**前提ログ**
```ruby
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件
```

**コード例**
```ruby 
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](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3490027/2806ec8d-cbb4-4520-98d8-e01ce2d92251.png)

```ruby
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件
```

**コード例**
```ruby 
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次元配列が与えられる。全体で最も多く登場する値と、その出現回数を出力せよ。
複数ある場合は値の小さい方を選ぶ。

```ruby
matrix = [
  [1, 2, 2],
  [3, 1, 2],
  [4, 2, 1],
  [1, 4, 3]
]
```
**期待される出力：**
```ruby
1 4
 ```
 
**解答コード**
```ruby
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方向すべてより値が大きいセルの数を数えてください。

```ruby
grid = [
  [1, 3, 2],
  [5, 4, 6],
  [3, 2, 1]
]
```
**期待される出力：**
```ruby
2
 ```
（セル(1,0)の5と、(1,2)の6が「山」）

 
**解答コード**
```ruby
# 入力となる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次元配列の中で、右下方向に位置する同じ値のペアの数を数えてください。

```ruby
matrix = [
  [1, 2, 3],
  [4, 1, 6],
  [7, 8, 1]
]
```
**期待される出力：**
```ruby
2
 ```
（(0,0)-(1,1)、(1,1)-(2,2) が「1」）

 
**解答コード**
```ruby
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個以上含まれるブロックの個数を数えてください。

```ruby
grid = [
  [1, 0, 1, 1],
  [0, 0, 1, 0],
  [1, 1, 0, 1]
]
```
**期待される出力：**
```ruby
5
 ```
 
**解答コード**
```ruby
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 の数を数えてください。

```ruby
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],
]
```
**期待される出力：**
```ruby
1
 ```
 
**解答コード**
```ruby
# 入力となる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
```

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

https://www.wantedly.com/companies/xincere-inc/stories

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