Ruby'de Array, sıfırdan başlayan tam sayı indeksleriyle erişilen, sıralı bir nesne koleksiyonudur. Dizi oluşturmak için köşeli parantez [], %w[] ya da Array.new kullanırsınız; sona eleman eklemek için push veya <<, sondan almak için pop, baştan almak için shift, başa eklemek için unshift çağırırsınız. flatten, compact, uniq ve sort yeni bir dizi döndürür; aynı adın sonuna ünlem eklenmiş biçimleri (compact!, uniq!, sort!) ise dizinin kendisini değiştirir.
Bu yazıda dizi oluşturmayı, indeksleme ve dilimlemeyi, ekleme ve çıkarma metotlarını, düzleştirme, temizleme ve sıralamayı Ruby 4.0 ile çalıştırılmış örneklerle anlatıyoruz. Son bölümde ünlemli (yıkıcı) ve ünlemsiz metotların farkını ve bu farktan doğan yaygın hataları ele alıyoruz. Ruby'ye yeni başlıyorsanız önce Ruby öğrenme rehberine, dilin genel tanıtımı için de Ruby nedir yazısına göz atabilirsiniz.
Dizi oluşturmanın yolları

En sık kullanılan yol dizi literalidir. Bir dizinin elemanları farklı türlerden olabilir, hatta başka bir dizi de içerebilir. Yalnızca kelimelerden oluşan diziler için %w[], sembol dizileri için %i[] yazımı daha kısadır. Kernel#Array metodu ise verdiğiniz değeri diziye çevirir; nil boş diziye, tek bir değer tek elemanlı diziye dönüşür.
karisik = [1, "bir", :bir, [2, "iki"]]
karisik.size # => 4
%w[elma armut kiraz] # => ["elma", "armut", "kiraz"]
%i[ad soyad yas] # => [:ad, :soyad, :yas]
Array.new(3) # => [nil, nil, nil]
Array.new(3) { |i| i * 10 } # => [0, 10, 20]
Array(nil) # => []
Array(1..4) # => [1, 2, 3, 4]Array.new(3, deger) biçimi her konuma aynı nesneyi yerleştirir. Belgeler bu kullanımı yalnızca sayı, sembol, nil, true ve false gibi değiştirilemeyen değerler için önerir. Değiştirilebilir bir nesne (dizi, hash, string) istiyorsanız blok biçimini kullanın; blok her konum için ayrı bir nesne üretir:
ortak = Array.new(3, [])
ortak[0] << 1
ortak # => [[1], [1], [1]]
ayri = Array.new(3) { [] }
ayri[0] << 1
ayri # => [[1], [], []]İndeksleme ve dilimleme: [], slice, first, last
İndeksler 0'dan başlar. Negatif indeks sondan sayar: -1 son eleman, -2 sondan bir önceki elemandır. Aralık dışındaki bir indeks hata vermez, nil döner. [] metodu tek indeks, başlangıç ve uzunluk çifti ya da bir aralık (Range) kabul eder; slice bu metodun diğer adıdır.
arr = [10, 20, 30, 40, 50, 60]
arr[0] # => 10
arr[-1] # => 60
arr[100] # => nil
arr[1, 3] # => [20, 30, 40]
arr[1..3] # => [20, 30, 40]
arr[1...3] # => [20, 30]
arr[2..] # => [30, 40, 50, 60]
arr.slice(1, 2) # => [20, 30]
arr.first # => 10
arr.first(2) # => [10, 20]
arr.last(2) # => [50, 60]
arr.take(3) # => [10, 20, 30]
arr.drop(4) # => [50, 60]1..3 ile 1...3 arasındaki fark (bitiş dahil mi, hariç mi) ve 2.. gibi sonu açık aralıklar Range yazısında ayrıntılı anlatılıyor. Eksik bir indeksin sessizce nil dönmesini istemiyorsanız fetch kullanın: indeks yoksa IndexError fırlatır ya da verdiğiniz varsayılan değeri döndürür.
arr = [10, 20, 30, 40, 50, 60]
arr.fetch(1) # => 20
arr.fetch(100, "yok") # => "yok"
begin
arr.fetch(100)
rescue IndexError => e
puts e.message # çıktı: index 100 outside of array bounds: -6...6
endİç içe dizilerde birden çok seviyeye inmek için dig kullanılabilir; yol üzerindeki herhangi bir adım nil ise sonuç da nil olur: [[1, [2, 3]]].dig(0, 1, 0) ifadesi 2 döndürür.
Eleman ekleme ve çıkarma: push, pop, shift, unshift

Bu dört metot diziyi yerinde değiştirir. push (diğer adı append) ve << sona ekler ve dizinin kendisini döndürür. pop sondaki elemanı çıkarıp döndürür, shift aynı işi baştan yapar. unshift (diğer adı prepend) başa ekler. insert ise istediğiniz konuma bir ya da daha fazla eleman koyar.
yigin = [1, 2, 3]
yigin.push(4, 5) # => [1, 2, 3, 4, 5]
yigin << 6 # => [1, 2, 3, 4, 5, 6]
yigin.pop # => 6
yigin.pop(2) # => [4, 5]
yigin.unshift(0) # => [0, 1, 2, 3]
yigin.shift # => 0
yigin.insert(1, :a, :b) # => [1, :a, :b, 2, 3]
[].pop # => nil
[].shift # => nilBoş bir diziden pop veya shift yapmak hata vermez, nil döner. pop(2) ve shift(2) gibi sayı alan biçimler ise her zaman bir dizi döndürür. push bir diziyi argüman olarak aldığında onu açmaz, tek bir eleman olarak ekler; elemanları tek tek eklemek için concat ya da + kullanın:
[1].push([2, 3]) # => [1, [2, 3]]
[1].concat([2, 3]) # => [1, 2, 3]
[1, 2, 3] + [3, 4] # => [1, 2, 3, 3, 4]
[1, 2, 3] - [2] # => [1, 3]
[1, 2, 3] & [2, 3, 4] # => [2, 3]
[1, 2, 3] | [3, 4] # => [1, 2, 3, 4]Belirli bir konumdaki elemanı silmek için delete_at, belirli bir değere eşit bütün elemanları silmek için delete, bir bölümü kesip almak için slice! kullanılır. Üçü de silinen değeri döndürür.
Düzleştirme, temizleme ve tekilleştirme: flatten, compact, uniq
flatten iç içe dizileri tek seviyeye indirir; isteğe bağlı derinlik argümanıyla kaç seviye açılacağını sınırlayabilirsiniz. compact yalnızca nil elemanları atar; false, 0 ve boş string yerinde kalır. uniq tekrar eden elemanları atar ve her değerin ilk görüldüğü konumu korur. Blok verirseniz karşılaştırma bloğun döndürdüğü değere göre yapılır.
ic_ice = [1, [2, [3, [4]]]]
ic_ice.flatten # => [1, 2, 3, 4]
ic_ice.flatten(1) # => [1, 2, [3, [4]]]
[1, nil, 2, nil, false, 0, ""].compact # => [1, 2, false, 0, ""]
[3, 1, 3, 2, 1].uniq # => [3, 1, 2]
%w[elma Elma armut].uniq { |s| s.downcase } # => ["elma", "armut"]Sıralama ve arama: sort, sort_by, include?, find
sort elemanları <=> operatörüyle karşılaştırır. Blok verirseniz karşılaştırmayı siz belirlersiniz: blok negatif bir sayı döndürürse a öne geçer, pozitif dönerse b. Çoğu durumda sort_by daha okunaklıdır; her eleman için bir sıralama anahtarı döndürmeniz yeterlidir. Belgelere göre eşit kabul edilen elemanların sırası garanti edilmez.
sayilar = [5, 3, 9, 1]
sayilar.sort # => [1, 3, 5, 9]
sayilar.sort { |a, b| b <=> a } # => [9, 5, 3, 1]
sayilar.max # => 9
sayilar.min(2) # => [1, 3]
sayilar.sum # => 18
%w[karpuz elma muz].sort_by { |s| s.size } # => ["muz", "elma", "karpuz"]Arama tarafında include? elemanın dizide olup olmadığını, index ilk konumunu, rindex son konumunu verir. find bloğu sağlayan ilk elemanı döndürür. Ruby 4.0 NEWS dosyasına göre Array#rfind eklendi ve Array#find artık Enumerable#find metodunun daha verimli bir Array sürümü olarak tanımlı. rfind, reverse_each.find yazmak yerine sondan aramayı doğrudan yapar.
renkler = %w[kırmızı yeşil mavi yeşil]
renkler.include?("mavi") # => true
renkler.index("yeşil") # => 1
renkler.rindex("yeşil") # => 3
[1, 2, 3, 4, 5, 6].find { |x| x.even? } # => 2
[1, 2, 3, 4, 5, 6].rfind { |x| x.even? } # => 6
[0, 1, 2].include?(2.0) # => trueYıkıcı ve yıkıcı olmayan metotlar: ünlem işareti ne anlatır?

Ruby'de bir metodun adının sonundaki !, aynı adlı ünlemsiz metoda göre "daha tehlikeli" bir sürüm olduğunu anlatır. Array için bu genellikle şu demektir: ünlemsiz metot yeni bir dizi döndürür ve asıl diziye dokunmaz, ünlemli metot ise diziyi yerinde değiştirir. Önemli bir ayrıntı daha var: compact!, uniq! ve flatten! gibi metotlar hiçbir değişiklik yapmadıklarında self yerine nil döndürür.
liste = [3, nil, 1, 3]
temiz = liste.compact
temiz # => [3, 1, 3]
liste # => [3, nil, 1, 3]
liste.compact! # => [3, 1, 3]
liste # => [3, 1, 3]
liste.compact! # => nil
liste.uniq! # => [3, 1]
liste.uniq! # => nil
liste.sort! # => [1, 3]Bu nil dönüşü, ünlemli metotları zincirlediğinizde sorun çıkarır. Aşağıdaki örnekte dizide tekrar eden eleman olmadığı için uniq! nil döndürür ve zincirin devamı hata verir:
veriler = [3, 1, 2]
begin
veriler.uniq!.sort!
rescue NoMethodError => e
puts e.message # çıktı: undefined method 'sort!' for nil
end
veriler.uniq.sort # => [1, 2, 3]| Yeni dizi döndürür | Diziyi değiştirir | Değişiklik yoksa ünlemli sürüm |
|---|---|---|
| compact | compact! | nil döner |
| uniq | uniq! | nil döner |
| flatten | flatten! | nil döner |
| sort | sort! | self döner |
| map | map! | self döner |
| select / reject | select! / reject! | nil döner |
| reverse | reverse! | self döner |
Bazı yıkıcı metotların adında ünlem yoktur: push, pop, shift, unshift, insert, delete, delete_if, keep_if ve clear diziyi doğrudan değiştirir. Yani ünlem işareti "bu metot diziyi değiştirir" listesinin tamamı değildir; belgelerde metotlar "Methods for Assigning" ve "Methods for Deleting" başlıkları altında ayrıca listelenir. Bir diziyi hiç değiştirilemez yapmak isterseniz freeze çağırın; ayrıntılar frozen string ve freeze yazısında.
sabit = [1, 2].freeze
begin
sabit << 3
rescue FrozenError => e
puts e.message # çıktı: can't modify frozen Array: [1, 2]
endYaygın hatalar
- Ünlemli metodun dönüş değerini kullanmak:
x = liste.uniq!yazarsanız tekrar yoksaxnilolur. Sonucu değişkene atayacaksanız ünlemsiz sürümü kullanın. - Array.new(n, []) ile tablo kurmak: bütün satırlar aynı diziyi paylaşır.
Array.new(n) { [] }yazın. - push ile dizi birleştirmek:
push([2, 3])iç içe dizi üretir; birleştirmek içinconcatveya+kullanın. - compact ile false değerleri temizlemeyi beklemek:
compactyalnızcanilatar. Yanlış (falsy) değerleri atmak içinselect(&:itself)gibi bir blok gerekir. - Döngü içinde diziyi değiştirmek:
eachile gezerken aynı dizidendeleteyapmak elemanların atlanmasına yol açabilir; bunun yerinerejectveyadelete_ifkullanın.
map, select, reduce gibi her koleksiyonda ortak olan metotlar Array'e Enumerable modülünden gelir; bunları Enumerable yazısında ayrıca ele alıyoruz.
Sık Sorulan Sorular
push ile << arasında fark var mı?
İkisi de sona ekler ve diziyi döndürür. Fark, push metodunun birden fazla argüman alabilmesidir: a.push(1, 2). << tek bir nesne ekler ama zincirlenebilir: a << 1 << 2.
length, size ve count aynı şey mi?
Argümansız çağrıldığında üçü de eleman sayısını verir. count ayrıca bir değer ya da blok alabilir: [1, 2, 2].count(2) sonucu 2, [1, 2, 3].count(&:odd?) sonucu 2 olur.
Diziyi kopyalamadan değiştirmek mi, kopyalayıp değiştirmek mi?
Dizi başka bir yerde de kullanılıyorsa (örneğin bir metoda argüman olarak geldiyse) ünlemsiz metotları tercih edin; çağıranın verisini beklenmedik şekilde değiştirmezsiniz. Yalnızca kendi oluşturduğunuz ve başka kimsenin tutmadığı bir diziyle çalışıyorsanız ünlemli sürüm ara kopya oluşturmaz.
Ruby 4.0 ile Array tarafında ne değişti?
NEWS dosyasında Array için iki madde var: Array#rfind eklendi ve Array#find Array'e özel olarak tanımlandı. Diğer Ruby 4.0 değişiklikleri için Ruby 4.0 yenilikler yazısına bakabilirsiniz.
Marka adları sahiplerine aittir. Bu yazı bağımsız bir teknik rehberdir; adı geçen kuruluşlarla bayilik veya ortaklık ilişkisi bulunduğu anlamına gelmez.
İlgili içerikler: Ruby öğrenme rehberi · Ruby Hash metotları · Enumerable: map, select, reduce · Ruby Range aralıklar · Frozen string ve freeze · Ruby Set sınıfı