Ruby'de Hash, her benzersiz anahtarı bir değere eşleyen bir koleksiyondur. Dizide indeks her zaman tam sayıyken hash'te anahtar neredeyse her nesne olabilir; en sık sembol ve string kullanılır. Değer okumak için h[:anahtar] (anahtar yoksa nil ya da varsayılan değer döner), anahtarın mutlaka bulunması gerektiğinde fetch (yoksa KeyError), iç içe yapılarda dig kullanırsınız. transform_values, transform_keys, merge ve select yeni bir hash döndürür.
Bu yazıda hash oluşturmayı, varsayılan değerleri, [] ile fetch farkını, gezinme ve dönüştürme metotlarını ve group_by sonucunun nasıl göründüğünü çalışan örneklerle anlatıyoruz. Örneklerdeki çıktılar Ruby 4.0 biçimindedir; Ruby 3.4 ile değişen inspect görünümüne de değineceğiz. Seriye baştan başlamak için Ruby öğrenme rehberine bakabilirsiniz.
Hash oluşturma ve Ruby 3.4 sonrası inspect biçimi

Klasik yazım "hash rocket" denen => işaretidir ve her türden anahtarla çalışır. Anahtar bir sembolse daha kısa olan anahtar: deger yazımını kullanabilirsiniz. Değeri atlayıp {x:, y:} yazarsanız Ruby değeri aynı adlı yerel değişkenden alır.
kullanici = {ad: "Ayşe", yas: 31}
kullanici # => {ad: "Ayşe", yas: 31}
{:ad => "Ayşe"} # => {ad: "Ayşe"}
karisik = {"şehir" => "İzmir", 1 => "bir"}
karisik # => {"şehir" => "İzmir", 1 => "bir"}
x = 1
y = 2
{x:, y:} # => {x: 1, y: 2}
puts kullanici.inspect # çıktı: {ad: "Ayşe", yas: 31}
Hash.new.class # => HashHash, girişleri eklendikleri sırayla tutar. Var olan bir anahtarın değerini güncellemek sırayı değiştirmez; bir anahtarı silip yeniden eklerseniz sona geçer. String anahtarlar için küçük bir güvence de var: dondurulmamış bir string'i anahtar yaptığınızda hash onun dondurulmuş bir kopyasını saklar, böylece anahtarı sonradan değiştirip hash'i bozamazsınız.
[] ile fetch farkı ve varsayılan değerler
h[anahtar] anahtar yoksa hash'in varsayılan değerini döndürür; varsayılan değer verilmemişse bu nildir. fetch ise varsayılan değere bakmaz: anahtar yoksa ikinci argümanı ya da bloğun sonucunu döndürür, ikisi de yoksa KeyError fırlatır. Yapılandırma okurken eksik bir anahtarı erken fark etmek için fetch daha güvenlidir.
stok = {elma: 12, armut: 0}
stok[:kiraz] # => nil
stok.fetch(:elma) # => 12
stok.fetch(:kiraz, 0) # => 0
stok.fetch(:kiraz) { |k| "#{k} yok" } # => "kiraz yok"
stok.key?(:armut) # => true
begin
stok.fetch(:kiraz)
rescue KeyError => e
puts e.message # çıktı: key not found: :kiraz
endVarsayılan değeri Hash.new(deger) ile verirsiniz. Saymak için Hash.new(0) klasik bir kalıptır. Bu değer yalnızca [], dig ve values_at için geçerlidir; fetch onu görmez.
sayac = Hash.new(0)
%w[a b a c a].each { |harf| sayac[harf] += 1 }
sayac # => {"a" => 3, "b" => 1, "c" => 1}
sayac["z"] # => 0
sayac.fetch("z", nil) # => nilyanlis = Hash.new([])
yanlis[:a] << 1
yanlis[:b] << 2
yanlis # => {}
yanlis[:c] # => [1, 2]
dogru = Hash.new { |hash, anahtar| hash[anahtar] = [] }
dogru[:a] << 1
dogru[:b] << 2
dogru # => {a: [1], b: [2]}Ruby 3.4 ile Hash.new isteğe bağlı bir capacity: argümanı da kabul ediyor; çok sayıda giriş ekleyeceğinizi biliyorsanız yer önceden ayrılır. Davranışı değiştirmez, yalnızca yeniden boyutlandırmayı azaltır.
İç içe veriye güvenli erişim: dig
JSON'dan ya da yapılandırma dosyasından gelen iç içe hash'lerde h[:a][:b][:c] zinciri, ara adımlardan biri eksikse nil üzerinde [] çağırmaya çalışır ve hata verir. dig her adımı kontrol eder, yol koptuğunda nil döner. Dizilerle karışık yapılarda indeks de verebilirsiniz.
ayar = {veritabani: {birincil: {host: "db1", portlar: [5432, 5433]}}}
ayar.dig(:veritabani, :birincil, :host) # => "db1"
ayar.dig(:veritabani, :birincil, :portlar, 1) # => 5433
ayar.dig(:veritabani, :yedek, :host) # => nil
begin
ayar[:veritabani][:yedek][:host]
rescue NoMethodError => e
puts e.message # çıktı: undefined method '[]' for nil
endJSON verisini hash'e çevirme adımı için Ruby JSON kullanımı yazısına bakın; orada anahtarların string mi sembol mü geleceğini belirleyen seçenek de anlatılıyor.
Gezinme ve dönüştürme: each_pair, to_a, transform_values, transform_keys

each ve onun diğer adı each_pair bloğa anahtarı ve değeri birlikte verir. keys ve values birer dizi döndürür, to_a hash'i [anahtar, deger] çiftlerinden oluşan bir diziye çevirir. to_h bu dönüşümün tersini yapar.
fiyat = {kalem: 15, defter: 40, silgi: 8}
fiyat.each_pair { |urun, tl| puts "#{urun}: #{tl} TL" }
# çıktı: kalem: 15 TL
# çıktı: defter: 40 TL
# çıktı: silgi: 8 TL
fiyat.keys # => [:kalem, :defter, :silgi]
fiyat.values # => [15, 40, 8]
fiyat.to_a # => [[:kalem, 15], [:defter, 40], [:silgi, 8]]
[[:a, 1], [:b, 2]].to_h # => {a: 1, b: 2}transform_values anahtarları korur, her değeri bloğun sonucuyla değiştirir. transform_keys anahtarları dönüştürür; blok yerine bir eşleme hash'i de alabilir. İkisinin de ünlemli sürümleri (transform_values!, transform_keys!) hash'i yerinde değiştirir.
fiyat = {kalem: 15, defter: 40, silgi: 8}
fiyat.transform_values { |tl| tl * 2 } # => {kalem: 30, defter: 80, silgi: 16}
fiyat.transform_keys(&:to_s) # => {"kalem" => 15, "defter" => 40, "silgi" => 8}
fiyat.transform_keys(kalem: :tukenmez) # => {tukenmez: 15, defter: 40, silgi: 8}
fiyat # => {kalem: 15, defter: 40, silgi: 8}Süzme: select, reject, filter_map, slice, except
Burada dönüş türüne dikkat etmek gerekir. Hash kendi select, reject, slice ve except metotlarını tanımlar ve bunlar hash döndürür. filter_map, map, min_by gibi Enumerable'dan gelen metotlar ise her girişi [anahtar, deger] çifti olarak görür ve dizi döndürür.
fiyat = {kalem: 15, defter: 40, silgi: 8}
fiyat.select { |_, tl| tl > 10 } # => {kalem: 15, defter: 40}
fiyat.reject { |_, tl| tl > 10 } # => {silgi: 8}
fiyat.slice(:kalem, :yok) # => {kalem: 15}
fiyat.except(:defter) # => {kalem: 15, silgi: 8}
fiyat.filter_map { |urun, tl| urun if tl < 20 } # => [:kalem, :silgi]
fiyat.map { |urun, tl| "#{urun}=#{tl}" } # => ["kalem=15", "defter=40", "silgi=8"]
fiyat.min_by { |_, tl| tl } # => [:silgi, 8]
fiyat.sum { |_, tl| tl } # => 63
fiyat.sort_by { |_, tl| -tl }.to_h # => {defter: 40, kalem: 15, silgi: 8}Birleştirme: merge ve merge!
merge iki ya da daha fazla hash'i birleştirip yeni bir hash döndürür; aynı anahtar varsa sağdaki değer kazanır. Çakışmada ne olacağına siz karar vermek istiyorsanız blok verin: blok anahtarı, eski değeri ve yeni değeri alır. merge! (diğer adı update) aynı işi soldaki hash'in üzerinde yapar.
varsayilan = {renk: "mavi", boyut: 12}
secim = {boyut: 14}
varsayilan.merge(secim) # => {renk: "mavi", boyut: 14}
varsayilan # => {renk: "mavi", boyut: 12}
sepet = {elma: 3}
ek = {elma: 2, armut: 1}
sepet.merge(ek) { |_anahtar, eski, yeni| eski + yeni } # => {elma: 5, armut: 1}
sepet.merge!(ek) # => {elma: 2, armut: 1}
sepet # => {elma: 2, armut: 1}group_by ve tally sonucu nasıl görünür?
group_by bir Enumerable metodudur ve sonucu her zaman bir hash'tir: anahtarlar bloğun döndürdüğü değerler, değerler ise o anahtara düşen elemanların dizisidir. Sonucu genellikle transform_values ile sadeleştirirsiniz. Yalnızca sayı istiyorsanız tally daha kısadır.
ogrenciler = [
{ad: "Can", sinif: "9A"},
{ad: "Ece", sinif: "9B"},
{ad: "Ali", sinif: "9A"}
]
gruplar = ogrenciler.group_by { |o| o[:sinif] }
gruplar.keys # => ["9A", "9B"]
gruplar["9B"] # => [{ad: "Ece", sinif: "9B"}]
gruplar.transform_values { |liste| liste.map { |o| o[:ad] } } # => {"9A" => ["Can", "Ali"], "9B" => ["Ece"]}
ogrenciler.map { |o| o[:sinif] }.tally # => {"9A" => 2, "9B" => 1}Bu metotların ayrıntıları ve each_with_object, partition gibi akrabaları için Enumerable yazısına bakın.
Silme, sayma ve sorgulama
delete bir anahtarı silip değerini döndürür; anahtar yoksa nil döner. Koşula göre toplu silmek için delete_if (ya da ünlemli reject!) kullanılır; ikisi de hash'i yerinde değiştirir. size giriş sayısını, empty? boş olup olmadığını verir. any? ve count gibi Enumerable metotları blokla birlikte koşul sorgulamak için kullanılabilir. key metodu ise bir değere karşılık gelen ilk anahtarı bulur.
puan = {ali: 70, ece: 95, can: 40}
puan.size # => 3
puan.any? { |_, p| p < 50 } # => true
puan.count { |_, p| p >= 70 } # => 2
puan.key(95) # => :ece
puan.values_at(:ali, :yok) # => [70, nil]
puan.delete(:can) # => 40
puan.delete(:yok) # => nil
puan.delete_if { |_, p| p < 80 } # => {ece: 95}
puan.empty? # => false
puan.clear # => {}Yerinde değiştiren metotlarla yeni hash döndürenler arasındaki ayrım Array ile aynı mantıktadır: reject yeni bir hash verir, reject! ve delete_if var olanı değiştirir. Hash başka bir yerde de kullanılıyorsa ünlemsiz sürümü seçin.
Yaygın hatalar
![Yaygın hatalar: Sembol ve string anahtarı karıştırmak, Hash.new([]) ya da Hash.new({}) kullanmak, fetch'in varsayılan değeri kullanacağını sanmak, select sonucunu dizi sanmak](/assets/blog/ruby-hash-metotlari-b9.webp)
- Sembol ve string anahtarı karıştırmak:
{ad: 1}["ad"]nildöner;:adile"ad"farklı anahtarlardır. Ayrıntı: Symbol ve String farkı. - Hash.new([]) ya da Hash.new({}) kullanmak: bütün eksik anahtarlar aynı nesneyi paylaşır. Blok biçimini kullanın.
- fetch'in varsayılan değeri kullanacağını sanmak:
Hash.new(0).fetch(:x)yineKeyErrorverir. - select sonucunu dizi sanmak: Hash#select hash döndürür, ama
filter_mapvemapdizi döndürür. - Anahtar olarak kullanılan bir diziyi değiştirmek: belgelere göre bu hash'in iç dizinini bozar ve anahtar bulunamaz hale gelir; düzeltmek için
rehashgerekir.
Sık Sorulan Sorular
Hash'te sıralama korunur mu?
Evet. Hash girişleri eklendikleri sırayla gezilir; each, keys, values ve inspect bu sırayı izler. Değere göre sıralı bir görünüm için sort_by ile dizi alıp gerekirse to_h ile yeniden hash'e çevirin.
Bir anahtarın var olup olmadığını nasıl anlarım?
key? (diğer adları has_key?, include?, member?) kullanın. h[k].nil? kontrolü, değeri bilinçli olarak nil olan bir anahtarla hiç olmayan anahtarı ayırt edemez.
Kendi sınıfımın nesnelerini hash anahtarı yapabilir miyim?
Evet, ama iki nesnenin aynı anahtar sayılması için sınıfın hash ve eql? metotlarını birlikte tanımlaması gerekir. Belgelere göre iki nesne, hash değerleri aynı ve birbirlerine eql? ise aynı anahtar kabul edilir. Bu metotları hazır getiren Struct ve Data için Struct ve Data yazısına bakabilirsiniz.
Hash'i dondurmak ne işe yarar?
freeze çağrılmış bir hash'e anahtar eklemek veya değer değiştirmek FrozenError verir. Sabit tablolar için kullanışlıdır; ayrıntılar freeze yazısında.
Ruby 4.0 Hash için yeni bir şey getirdi mi?
Ruby 4.0 NEWS dosyasında Hash için ayrı bir madde yok. Görünür son değişiklikler Ruby 3.4'te geldi: yeni inspect biçimi ve Hash.new için capacity: argümanı.
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 Array metotları · Enumerable: map, select, reduce · Symbol ve String farkı · Ruby JSON kullanımı · Pattern matching