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ı

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.

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

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

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

ruby
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

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.

ruby
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 # => nil

Boş 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:

ruby
[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.

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

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

ruby
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) # => true

Yıkıcı ve yıkıcı olmayan metotlar: ünlem işareti ne anlatır?

Yıkıcı ve yıkıcı olmayan metotlar: ünlem işareti ne anlatır? compact, uniq, flatten, sort

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.

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

ruby
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ürDiziyi değiştirirDeğişiklik yoksa ünlemli sürüm
compactcompact!nil döner
uniquniq!nil döner
flattenflatten!nil döner
sortsort!self döner
mapmap!self döner
select / rejectselect! / reject!nil döner
reversereverse!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.

ruby
sabit = [1, 2].freeze
begin
  sabit << 3
rescue FrozenError => e
  puts e.message # çıktı: can't modify frozen Array: [1, 2]
end

Yaygın hatalar

  • Ünlemli metodun dönüş değerini kullanmak: x = liste.uniq! yazarsanız tekrar yoksa x nil olur. 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çin concat veya + kullanın.
  • compact ile false değerleri temizlemeyi beklemek: compact yalnızca nil atar. Yanlış (falsy) değerleri atmak için select(&:itself) gibi bir blok gerekir.
  • Döngü içinde diziyi değiştirmek: each ile gezerken aynı diziden delete yapmak elemanların atlanmasına yol açabilir; bunun yerine reject veya delete_if kullanı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ı