อาเรย์ ในภาษา Ruby

ในบทนี้ คุณจะได้เรียนรู้เกี่ยวกับอาเรย์ในภาษา Ruby เราจะพูดเกี่ยวกับการประกาศและใช้งานอาเรย์ และการใช้งานเมธอดที่สำคัญของอาเรย์เพื่อใช้จัดการข้อมูลในอาเรย์ และก่อนที่จะเริ่ม มาทำความรู้จักกันก่อนว่าอาเรย์คืออะไร

อาเรย์ (Array) คือข้อมูลแบบชุดลำดับที่สามารถเก็บค่าได้หลายค่าในตัวแปรเดียว อาเรย์มี index ที่ใช้สำหรับอ้างถึงข้อมูลในตัวแปรอาเรย์ นั่นทำให้เราสามารถจัดการกับค่าในอาเรย์ได้เหมือนกับว่ามันเป็นตัวแปรปกติ อาเรย์นั้นเป็นออบเจ็คจากคลาส Array ซึ่งมีเมธอดต่างๆ เป็นจำนวนมากสำหรับทำงานกับอาเรย์

การประกาศอาเรย์

วิธีที่ง่ายที่สุดในการประกาศอาเรย์คือการใช้อาเรย์ Literal นี่เป็นรูปแบบการประกาศอาเรย์ในภาษา Ruby

array_name = [object1, object2, object3, ...]

โดยที่ name นั้นเป็นชื่อของตัวแปรอาเรย์ และหลังจากนั้นเป็นการกำหนดค่าให้กับอาเรย์ โดยสมาชิกของอาเรย์จะต้องอยู่ในเครื่องหมายวงเล็บ [ ... ] และคั่นสมาชิกแต่ละตัวด้วยเครื่องหมายคอมมา (,) นี่เป็นตัวอย่างของการประกาศอาเรย์ในภาษา Ruby

numbers = [10, 20, 30, 40, 50]
fruits = ["Apple", "Lemon", "Orange"]
basket = []

ในตัวอย่าง เราได้ประกาศตัวแปรอาเรย์มาสามตัว ซึ่งแต่ละตัวนั้นเก็บข้อมูลที่แตกต่างกัน ตัวแปรอาเรย์ numbers นั้นเก็บตัวเลขจำนวนเต็ม 5 ตัว ดังนั้นเราสามารถกล่าวได้ว่าอาเรย์นี้มีสมาชิก 5 ตัว ส่วนตัวแปรอาเรย์ fruits นั้นเก็บค่าที่เป็น String จำนวน 3 ตัว

สุดท้ายตัวแปรอาเรย์ basket ในตัวแปรนี้เราไม่ได้กำหนดสมาชิกใดๆ ให้กับมันเลย ดังนั้นจึงถือว่ามันเป็นอาเรย์ว่างหรือไม่มีสมาชิก ซึ่งในทางปฏิบัติ เราอาจจะประกาศอาเรย์ว่างและกำหนดค่าให้มันในภายหลังได้

หลังจากที่ประกาศอาเรย์ไปแล้ว คุณสามารถใส่ออบเจ็คเข้าไปเพิ่มในอาเรย์ได้ โดยการใช้เมธอด push หรือเครื่องหมาย << ซึ่งทั้งสองรูปแบบมีความหมายเหมือนกัน เมื่อเราได้เพิ่มข้อมูลเข้าไปในอาเรย์ ข้อมูลจะไปเข้าต่อท้ายสมาชิกสุดท้ายของอาเรย์

scores = [10, 20, 30]
scores.push(40)
scores.push(50)
scores << 60
p scores    # => [10, 20, 30, 40, 50, 60]   

เนื่องจาก Ruby นั้นมีประเภทข้อมูลเป็นแบบไดนามิกส์ นั่นทำให้เราสามารถเก็บออบเจ็คประเภทที่แตกต่างกันในอาเรย์ได้ ตัวแปรอาเรย์ stuff ของเราได้เก็บตัวเลขจำนวนเต็ม เลขทศนิยม ข้อความ และออบเจ็คของเวลา อย่างไรก็ตาม ในการเขียนโปรแกรมส่วนมากแล้วเรามักจะใช้อาเรย์เก็บข้อมูลประเภทเดียวกัน ยกเว้นแต่ว่าคุณต้องการทำบางอย่างที่นอกเหนือจากนั้น

stuff = [1, 3.14, "Ruby", Time.now]
p stuff     # => [1, 3.14, "Ruby", 2020-01-19 03:12:45 +0700]

หลังจากที่ได้ประกาศอาเรย์ไปแล้ว ในบางครั้งเราก็อาจจะอยากทราบว่าอาเรย์นั้นมีสมาชิกจำนวนเท่าไร ต่อไปมาดูตัวอย่างสำหรับนับจำนวนสมาชิกภายในอาเรย์กัน

names = ["Meteo", "Luke", "Samuel"]

puts "Initialize array with three names"
puts names.length

names.push("Narcissus")
names.push("Ramsey")
puts "Pushed two names to the array"
puts names.length

นี่เป็นผลลัพธ์ของโปรแกรม

Initialize array with three names
3
Pushed two names to the array
5

ในตัวอย่างนั้นแสดงให้เห็นว่าเราสามารถใช้เมธอด length เพื่อนับจำนวนสมาชิกในอาเรย์ได้ เราได้ประกาศตัวแปรอาเรย์ names และเก็บชื่อจำนวนสามชื่อในตอนแรก และแน่นอนเมื่อเราเรียกใช้เมธอด length จำนวนวสมาชิกของอาเรย์นั้นมีค่าเป็น 3 หลังจากนั้นเราใส่อีกสองชื่อเข้าไปในอาเรย์ด้วยเมธอด push และตอนนี้อาเรย์มีสมาชิกทั้งหมด 5 ตัว

เทคนิค: เรามักจะใช้เมธอด p เพื่อดูข้อมูลในตัวแปรอาเรย์ ซึ่งมันเป็นวิธีที่ง่ายต่อการตรวจสอบข้อมูลที่ถูกเก็บในตัวแปรอาเรย์

การเข้าถึงและอ่านค่าในอาเรย์

ในการเข้าถึงสมาชิกภายในอาเรย์ โดยทั่วไปแล้วเราจะต้องทำผ่าน index ซึ่ง index ของอาเรย์นั้นเป็นตัวเลขจำนวนเต็มที่เริ่มจาก 0 ถึง n - 1 เมื่อ n เป็นจำนวนสมาชิกทั้งหมดของอาเรย์

numbers = [10, 20, 30, 40, 50]
puts numbers[0]
puts numbers[1]
puts numbers[4]

นี่เป็นผลลัพธ์ของโปรแกรม

10
20
50

ในตัวแปรอาเรย์ numbers นั้นมีสมาชิกทั้งหมดจำนวน 5 ตัว นั่นหมายความว่า index ของอาเรย์จะมีค่าตั้งแต่ 0 - 4 ดังนั้นการเข้าถึงค่าต่างๆ ภายในอาเรย์สามารถเขียนได้เป็น numbers[index] ดังนั้น numbers[0] เป็นการเข้าถึงสมาชิกตัวแรกในอาเรย์ numbers[1] เป็นการเข้าถึงสมาชิกตัวที่สอง ไปตามลำดับจนถึงสมาชิกตัวสุดท้าย ที่สามารถเข้าถึงได้โดย numbers[4]

นอกจากการอ่านค่าแล้ว เราสามารถเข้าถึงสมาชิกในอาเรย์เพื่อเปลี่ยนแปลงค่าได้เช่นเดียวกัน ดังในตัวอย่างต่อไปนี้

numbers = [10, 20, 30, 40, 50]
p numbers
numbers[0] = 15
numbers[2] = 35
numbers[4] = numbers[4] * 2
p numbers

นี่เป็นผลลัพธ์ของโปรแกรม

[10, 20, 30, 40, 50]
[15, 20, 35, 40, 100]

ในตัวอย่าง เราได้เปลี่ยนค่าสมาชิกตัวแรกและตัวที่สามในอาเรย์เป็น 15 และ 35 ตามลำดับ หลังจากนั้นเปลี่ยนค่าสมาชิกตัวสุดท้ายให้มีค่าเพิ่มขึ้นเป็นสองเท่า

ในตัวอย่างก่อนหน้าเป็นการเข้าถึงสมาชิกในอาเรย์ด้วย index ที่เป็นจำนวนเต็มบวก ในภาษา Ruby คุณสามารถเข้าถึงสมาชิกในอาเรย์ด้วย index ที่เป็นจำนวนเต็มลบได้ มาดูตัวอย่าง

fruits = ["Apple", "Lemon", "Orange", "Banana", "Pear"]
puts fruits[-1]
puts fruits[-2]
puts fruits[-5]
fruits[-5] = "Mango"
puts fruits[-5]

นี่เป็นผลลัพธ์ของโปรแกรม

Pear
Banana
Apple
Mango

คุณสามารถระบุ index ที่มีค่าเป็นลบเพื่อเข้าถึงสมาชิกในอาเรย์ได้ โดยที่ -1 เป็นการเข้าถึงสมาชิกชิกตัวสุดท้ายในอาเรย์ และ -2 เป็นสมาชิกรองสุดท้ายในอาเรย์ถัดมาเรื่อยๆ ในตัวอย่างเราได้ใช้ index ที่เป็นจำนวนเต็มลบกับอาเรย์ fruits และมันก็ทำงานได้ดี ซึ่งวิธีนี้อาจจะเป็นประโยชน์ได้ในกรณีที่เราต้องการเข้าถึงสมาชิกในอาเรย์โดยอ้างอิงจากตัวสุดท้าย ยกตัวอย่างเช่น

scores = [95, 82, 70, 63, 59, 50]
puts "Third lowest score: #{scores[-3]}"

เราต้องการทราบว่าคะแนนที่น้อยที่สุดเป็นอันดับที่สามในอาเรย์คืออะไร เราสามารถใช้ index ที่เป็นลบเพื่อเข้าถึงสมาชิกในตำแหน่งที่สามจากท้ายของอาเรย์ได้โดยไม่ต้องสนใจว่าขนาดของอาเรย์จะเป็นเท่าไหร่ อย่างไรก็ตาม จากตัวอย่างนั้นข้อมูลในอาเรย์ต้องเรียงจากมากไปน้อยแล้วเท่านั้น

การใช้คำสั่งวนซ้ำกับอาเรย์

อาเรย์นั้นเก็บข้อมูลเป็นแบบลำดับและข้อมูลเหล่านั้นสามารถเข้าถึงได้โดย index ของมัน นั้นจึงเป็นเรื่องที่สะดวกที่เราจะใช้คำสั่งวนซ้ำอย่างเช่น คำสั่ง for เพื่อวนอ่านค่าในตัวแปรอาเรย์ผ่านทาง index ของมัน นี่เป็นตัวอย่าง

names = ["Meteo", "Luke", "Samuel", "Narcissus", "Ramsey"]
for i in (0...names.length)
    puts names[i]
end

นี่เป็นผลลัพธ์การทำงานของโปรแกรม

Meteo
Luke
Samuel
Narcissus
Ramsey

ในตัวอย่าง เราได้ใช้คำสั่งวนซ้ำ for เพื่อวนอ่านค่าในตัวแปรอาเรย์ โดยการสร้าง index จากออบเจ็ค Range ซึ่ง index ของเราจะเริ่มจาก 0 ถึงขนาดของอาเรย์ names.length - 1 ซึ่งสามารถแสดงได้ด้วยออบเจ็ค (0...names.length) ในแต่ละรอบของการวนซ้ำ เราได้จะได้รับค่า index มาเก็บไว้ในตัวแปร i นั่นจึงทำให้เราสามารถนำ index ที่ได้ไปใช้กับตัวแปรอาเรย์เพื่ออานข้อมูลออกมาแสดงผลได้

มันขึ้นอยู่กับคุณว่าจะประยุกต์ใช้งานคำสั่งวนซ้ำจากอาเรย์อย่างไร จากในตัวอย่างก่อนหน้า เราสามารถแก้ไขโค้ดเพื่อให้โปรแกรมอ่านข้อมูลเริ่มจากสมาชิกตัวสุดท้ายของอาเรย์ได้

# Reading array in reverse order
# Method 1
names = ["Meteo", "Luke", "Samuel", "Narcissus", "Ramsey"]
for i in (0...names.length)
    puts names[(i + 1) * -1]
end

# Method 2
names = ["Meteo", "Luke", "Samuel", "Narcissus", "Ramsey"]
for i in (0...names.length)
    puts names[names.length - 1 - i]
end

จากทั้งสองตัวอย่างนั้นให้ผลลัพธ์ที่เหมือนกัน ตัวอย่างแรกเป็นการเข้าถึงสมาชิกในอาเรย์ด้วย index ที่เป็นลบ names[(i + 1) * -1] และตัวอย่างที่สองเป็นการสร้าง index แบบผันกลับด้วยการคำนวณจากขนาดของอาเรย์ names[names.length - 1 - i]

ต่อไปมาดูตัวอย่างการใช้งานคำสั่งวนซ้ำกับอาเรย์ เราจะเขียนโปรแกรมเพื่อหาจำนวนประกอบทั้งหมดของตัวเลขจำนวนเต็มที่รับเข้ามาจากคีย์บอร์ด

find_factor.rb
print "Enter number: "
n = gets.chomp.to_i

factors = []
for i in (1..n)
    factors.push(i) if n % i == 0
end

puts "There are #{factors.length} factors of #{n}:"
p factors

นี่เป็นผลลัพธ์ของโปรแกรม เมื่อเรากรอกค่าเป็น 78

Enter number: 78
There are 8 factors of 78:
[1, 2, 3, 6, 13, 26, 39, 78]

และนี่เป็นอีกผลลัพธ์ เมื่อเรากรอกค่าเป็น 245

Enter number: 245
There are 6 factors of 245:
[1, 5, 7, 35, 49, 245]

ในตัวอย่าง แสดงให้เห็นว่าเราสามารถใช้อาเรย์เพื่อเก็บค่าที่ได้จากการทำงานบางอย่างได้ โปรแกรมของเรารับค่า n เข้ามาทางคีย์บอร์ด และเพื่อหาตัวประกอบทั้งหมดของจำนวนเต็ม n เราต้องการหาว่าตั้งแต่ 1 - n นั้นมีตัวเลขไหนบ้างที่หาร n และถ้าหากว่าตัวเลขใดๆ หาร n ลงตัว มันถือว่าเป็นตัวประกอบของ n

factors.push(i) if n % i == 0

ในคำสั่งนี้เป็นการตรวจสอบว่าตัวเลขนั้นหาร n ลงตัวหรือไม่ และถ้าหากหารลงตัวเราใส่ตัวเลขดังกล่าวลงไปในตัวแปรอาเรย์ factors ของเรา สังเกตว่าในตอนแรกเราได้ประกาศให้มันเป็นอาเรย์ว่างก่อน เพื่อที่เราจะได้ใส่ค่าลงไปในภายหลังได้

ข้อควรรู้: ในตัวอย่างสังเกตว่าเราใช้ Range อยู่สองแบบคือแบบสามจุด (0...n) และแบบสองจุด (0..n) แบบแรกนั้นเป็นการสร้างลำดับตัวเลขที่ไม่รวม n ในขณะที่แบบที่สองจะรวม n ด้วย ดังนั้นเมื่อใช้ Range เพื่อสร้าง index ของอาเรย์ เรามักจะใช้แบบสามจุด เนื่องจากว่าสมาชิกตัวสุดท้ายในอาเรย์นั้นมี index เท่ากับ n - 1 เมื่อ n เป็นขนาดของอาเรย์

การวนรอบอาเรย์

ในตัวอย่างก่อนหน้า เราได้ใช้คำสั่งวนซ้ำ for เพื่อที่จะวนอ่านค่าในอาเรย์ ซึ่งในความเป็นจริงแล้ว Ruby ได้มีเมธอด each ที่เราสามารถใช้วนอ่านค่าในอาเรย์ได้เลย มาดูตัวอย่าง

names = ["Meteo", "Luke", "Samuel", "Narcissus", "Ramsey"]
names.each { |item|
    puts "#{item} #{item.length}"
}

นี่เป็นผลลัพธ์ของโปรแกรม เราได้แสดงชื่อในอาเรย์ออกมาและดูว่าแต่ละชื่อนั้นมีความยาวเท่าไหร่

Meteo 5
Luke 4
Samuel 6
Narcissus 9
Ramsey 6

ในตัวอย่าง เราได้ใช้เมธอด each ในการวนอ่านค่าในอาเรย์ เมธอดนี้รับบล็อคเป็นอาร์กิวเมนต์ นั่นหมายความว่าในแต่ละรอบของการวนในอาเรย์ โปรแกรมจะเรียกใช้งานบล็อคนี้ โดยส่งค่าสมาชิกในอาเรย์แต่ละตัวผ่านเข้ามาทางตัวแปร item ที่เราได้ประกาศไว้ในบล็อค

และถ้าหากคุณต้องการวนอ่านค่ามาจากสมาชิกตัวสุดท้ายของอาเรย์ คุณสามารถใช้เมธอด reverse_each เพื่อการทำงานนี้ได้

names = ["Meteo", "Luke", "Samuel", "Narcissus", "Ramsey"]
names.reverse_each { |item|
    puts "#{item} #{item.length}"
}

นี่เป็นผลลัพธ์การทำงานของโปรแกรม

Ramsey 6
Narcissus 9
Samuel 6
Luke 4
Meteo 5

ในบางกรณี เราอาจจะจำเป็นต้องนำ index ของอาเรย์มาใช้งานบางอย่าง ยกตัวอย่างเช่น เราต้องการสร้าง ID ของภาษาเขียนโปรแกรมด้วย index ของมันก่อนที่จะจัดเก็บไว้ในฐานข้อมูล หรือนำมาแสดงผล ซึ่งเราสามารถวนรอบ index ของอาเรย์ได้ด้วยเมธอด each_index

lang = ["Ruby", "PHP", "Python", "Perl"]
puts "ID\tLanguage"
lang.each_index { |i|
    puts "#{i + 1}\t#{lang[i]}"
}

นี่เป็นผลลัพธ์การทำงานของโปรแกรม

ID      Language
1       Ruby
2       PHP
3       Python
4       Perl

ในตัวอย่าง เราได้สร้าง ID ของแต่ละภาษาเขียนโปรแกรมด้วย index ของมัน และเนื่องจากว่าเมธอด each_index นั้นส่งเพียงค่า index เข้ามาในบล็อคเท่านั้น ในกรณีที่เราจะเข้าถึงค่าในอาเรย์ เราสามารถเข้าถึงค่าในรอบปัจจุบันได้ด้วยคำสั่ง lang[i]

สังเกตว่าการใช้งานเมธอด each_index นั้นมีความคล้ายคลึงกับการใช้คำสั่งวนซ้ำ for เพื่อวนอ่านค่าในอาเรย์มาก และแน่นอนว่าทั้งสองวิธีให้ผลลัพธ์ที่เหมือนกัน

อาเรย์ซ้อนอาเรย์

อาเรย์นั้นเป็นออบเจ็คที่สามารถเก็บข้อมูลได้ทุกประเภทในตัวมัน และแน่นอนว่าเราสามารถเก็บอาเรย์ไว้ในอาเรย์ได้ ซึ่งการทำเช่นนี้จะทำให้เกิดอาเรย์ซ้อนอาเรย์ หรืออาเรย์หลายมิติ (Multi-dimensional array) ขึ้นได้ ในบทนี้ เราจะพูดถึงอาเรย์ที่ซ้อนกันเพียงสองชั้นเท่านั้น

ลองจินตนาการว่า เราต้องการเขียนโปรแกรมที่เก็บคะแนนของนักเรียน 3 คน ซึ่งแต่ละคนนั้นจะมีคะแนนของ 4 วิชาที่เราจะต้องเก็บ นั่นหมายความว่าเราต้องการเก็บคะแนนทั้งหมด 12 ค่า ซึ่งโจทย์ปัญหานี้สามารถแก้ได้ด้วยการเก็บด้วยอาเรย์ซ้อนอาเรย์ โดยการทำเช่นนี้จะทำให้เราสามารถเก็บคะแนนทั้งหมดไว้ได้เพียงในตัวแปรอาเรย์ตัวแปรเดียว

นี่เป็นวิธีที่เราเก็บข้อมูลดังกล่าวในตัวแปรอาเรย์ซ้อนอาเรย์ในภาษ Ruby

scores = [
    [67, 45, 58, 75],
    [71, 38, 73, 91],
    [35, 32, 57, 89]
]

ในแต่ละแถวของอาเรย์ (แนวนอน) จะเป็นคะแนนของนักเรียนแต่ละคน ในขณะที่แต่ละหลักของอาเรย์ (แนวตั้ง) จะเป็นคะแนนของแต่ละวิชาที่นักเรียนสอบได้ ต่อไปเราจะวนอ่านค่าในตัวแปรอาเรย์ทั้งหมดออกมา

scores = [
    [67, 45, 58, 75],
    [71, 38, 73, 91],
    [35, 32, 57, 89]
]

for i in(0...scores.length)
    for j in (0...scores[i].length)
        print "#{scores[i][j]} "
    end
    puts
end

นี่เป็นผลลัพธ์ของโปรแกรม

67 45 58 75
71 38 73 91
35 32 57 89

ในตัวอย่าง เป็นการวนอ่านค่าอาเรย์ซ้อนอาเรย์ด้วยคำสั่งวนซ้ำ for สังเกตว่าเราจะต้องใช้คำสั่ง for ซ้อนกันเพื่อวนอ่านค่าในอาเรย์ที่ซ้อนกัน

for i in(0...scores.length)

ในคำสั่ง for รอบนอกนั้น เป็นการวนอ่านค่าของอาเรย์ในแต่ละแถว ดังนั้นเราสามารถเข้าถึงค่าในแต่ละแถวได้ด้วยคำสั่ง scores[i]

for j in (0...scores[i].length)

ต่อมาเราได้สร้างคำสั่ง for เพื่อวนอ่านค่าสมาชิกของอาเรย์ในแต่ละแถว และเข้าถึงสมาชิกในอาเรย์โดยอ้างอิงจากอาเรย์หลักได้ด้วยคำสั่ง scores[i][j]

จากตัวอย่างก่อนหน้า เนื่องจากเราทราบว่าเราเก็บข้อมูลของนักเรียนสามคน และเก็บทั้งหมดเป็นจำนวน 4 วิชา ดังนั้นเราสามารถจัดรูปแบบการแสดงผลใหม่เพื่อให้เราใจได้มากขึ้น เป็นดังนี้

names = ["Meteo", "Luke", "Sofia"]
subjects = ["Math", "Science", "Art", "History"]

scores = [
    [67, 45, 58, 75],
    [71, 38, 73, 91],
    [35, 32, 57, 89]
]

puts subjects.join("\t")
for i in(0...scores.length)
    for j in (0...scores[i].length)
        print "#{scores[i][j]}\t"
    end
    puts "#{names[i]}"
end

นี่เป็นผลลัพธ์การทำงานของโปรแกรม

Math    Science Art     History
67      45      58      75      Meteo
71      38      73      91      Luke
35      32      57      89      Sofia

สังเกตว่าผลลัพธ์ของโปรแกรมจะสอดคล้องกับที่เราได้บอกไปในตอนแรกว่า ในแต่ละแถวของอาเรย์ (แนวนอน) จะเป็นคะแนนของนักเรียนแต่ละคน ในขณะที่ในแต่ละหลักของอาเรย์ (แนวตั้ง) จะเป็นคะแนนของแต่ละวิชาที่นักเรียนสอบได้

ในบทนี้ คุณได้เรียนรู้เกี่ยวการสร้างและการใช้งานอาเรย์ในภาษา Ruby รวมถึงการเข้าถึงสมาชิกภายในอาเรย์ผ่านทาง index และด้วยคำสั่งวนซ้ำ หรือจะด้วยเมธอดของอาเรย์เอง อย่างไรก็ตาม ในภาษา Ruby นั้นยังมีเมธอดที่มีประโยชน์อีกเป็นจำนวนมากซึ่งคุณจะได้เรียนรู้ในบทต่อไป