Diziler(Arrayler)

Kodumuzda aynı türden bir veya iki farklı eleman kullanacaksak standart değişkenler yeterli olacaktır. Ancak bu sayı artarsa değişken kullanımı pek pratik olmamaya başlar. İşte bu nokta, dizilerin devreye girdiği yerdir.
Mesela 20 bölgesi olan bir bankada çalışıyorsanız her bölge için bir değişken tanımlamak yerine bölge adında bir dizi tanımlanıp, her bölge bu diziye elaman olarak atanabilir. Karşılaştırmaya bakalım:

						
									'pratik olmayan yöntem
									Dim bölge1 As String
									Dim bölge2 As String
									.....
									Dim bölge20 As String

							
						

Bir de bunun şube versiyonu var, ki şube sayısı 1000 civarındaysa değişken tanımlarken ömrünüzden 1 yıl gider herhalde.
Peki şimdi nedir bu diziler, nasıl tanımlanır, başka neler yapılır, bunlara bakalım.
Genel bir bilgi edindikten sonra buraya da bakmanızı tavsiye ederim. Özellikle başka programlama dillerine aşinaysanız aradaki farkları ve benzerlikleri görmek için faydalı olacağını düşünüyorum.

Tanımlama(Declaration)

Diziler, normal değişkenler gibi Dim ifadesi ile tanımlanırlar, ancak değişken adının yanında fazladan yuvarlak parantezlere sahiptirler. Parantezler; dizi boyutu baştan belliyse boyut numarasını içerirler, belli değilse boş bırakılır ve sonradan tanımlanır. Dizi boyutu baştan belirlenen dizilere Statik dizi, boyutu baştan belirtilmeyen dizilere Dinamik dizi denir.

						
								Dim diziadı(boyut) As Tip

							
						

Tip belirtilmezse tıpkı normal değişkenlerde olduğu gibi dizimiz Variant tipli bir dizi olur ve her tür değişkeni karışık olarak depolayabilir.

						
								Dim bölgeler(19) As String 'boyut belirtildi
								Dim şubekod() As String 'boyut belirtilmedi


							
						

Bu örnekte bölge sayısı sabit olduğu için boyut baştan belirtildi, yani Statik tanımlandı ancak bir bankada şube sayısı çok sık değişebilir; yeni şubeler açılır, mevcut şubeler kapanır, şubeler birleşir;o yüzden ona baştan bir boyut belirtmesek de olur, yani Dinamik tanımlandı. Bu dinamik dizilere aşağıda ayrıca bakıyor olacağız.

Boyut(elaman sayısı), index, alt/üst limitler

Dizi için tanımlanan eleman sayısına boyut denir. Boyut belirtmenin de iki yolu vardır.

  • Dim dizi(x to y) As Tip
  • Dim dizi(y) As Tip

İlk yöntemde dizinin kaçıncı indexten başlayıp kaçta biteceği belirtilirken ikinci yöntemde ise doğrudan kaçta biteceği belirtilir, kaçta başlayacağı ise Option Base ifadesinin kullanılıp kullanılmadığına göre değişir. İndeksin biteceği son yere üst sınır denir ve bu sınır Ubound fonksiyonu ile elde edilir.

						
								Dim segmentler(4) As String
								segmentler(0)="Bireysel"
								segmentler(1)="Birebir"
								segmentler(2)="Kobi"
								segmentler(3)="Ticari"
								'segmentler(4)="Özel"

								For i=0 to UBound(segmentler)
									MsgBox segmentler(i)
								Next i


							
						

Yukardaki örnekte gördüğünüz üzere son indexli elamana değer atanmadı, bu yüzden de boş olarak göründü. Anlayacağınız üzere, dizideki tüm elemanlara değer atanmak zorunda değildir. Yani, bir dizide eleman sayısı ile dolu(değer atanmış) eleman sayısı aynı olmayabilir.
Varsayılan olarak dizilerin ilk eleman indeksi 0'dır. Ancak bu index Option Base 1 ifadesi ile 1 yapılabilir, ki ben bunu zorunda olmadığınız sürece çok kullanmanızı tavsiye etmiyorum. Veya yukarda 1. yöntemdeki gibi dizi boyutu baştan 1 to y şeklinde belirtilerek de başlangıç indexi 1 yapılabilir. Yani şu iki ifade tamamen özdeştir.

						
								Option Base 1
								Dim bolgeler(20) As String



							
						
						
								Dim bolgeler(1 to 20) As String



							
						

İndex numarası açısından olmasa da eleman sayısı açısından şu dizi de yukardakilerle aynı kapasitededir, yani hepsi de 20 eleman içerir.

						
								Dim bolgeler(19) As String



							
						

Dizi tanımının yönteminden ve Option Base kullanımından bağımsız olarak alt indexin ne olduğu ise LBound fonksiyonu ile elde edilir.
Option Base 1 kullanımını tavsiye etmiyoruz dedik, zira ilgili modüldeki tüm prosedürler için indexi 1den başlatır. Bununla beraber bazı dizileri bilinçli olarak 1 nolu indeksten başlatmak gerekebilir. Mesela ayno isimli bir dizimiz olduğunu düşünün, ayno(1) diyince Ocak ayını ele almak, anlaşılırlık açısından daha makbuldür, pek tabiki ayno(1) içinde Şubat ayı da depolanabilir ancak bu yol, konuşma diline biraz aykırılık teşkil edeceği için bunu ayno(1 to 12) şeklinde tanımlamayı tercih etmek daha akıllıca olacaktır.
Bu arada x to y yönteminde x olarak 1'den büyük değerler de belirtilebilir ama bunun pratikte çok kullanıldığı görülmez.
Çok boyutlu dizilerde eleman sayısı, boyutlardaki elemanların çarpımına eşittir.(Bunlar aşağıda ayrıca detaylı incelenecek)
Özetleyecek olursak;
Dim b(10) As String şeklinde tanımlanan 1 boyutlu bir dizide;

Aranan Yöntem Sonuç
Alt limit LBound(b) 0
Üst limit UBound(b) 10
Eleman sayısı Ubound(b)-LBound(b)+1 11

Dim b(1 to 10) As String şeklinde tanımlanan 1 boyutlu bir başka dizide;

Aranan Yöntem Sonuç
Alt limit LBound(b) 1
Üst limit UBound(b) 10
Eleman sayısı Ubound(b)-LBound(b)+1 10

Dim b(10,5) As Integer şeklinde tanımlanan 2 boyutlu bir dizide;

Aranan Yöntem Sonuç
1.boyutun Alt limiti LBound(b,1) 0
1.boyutun Üst limiti UBound(b,1) 10
2.boyutun Alt limiti LBound(b,2) 0
2.boyutun Üst limiti UBound(b,2) 5
1.boyutun Eleman sayısı Ubound(b,1)-LBound(b,1)+1 11
2.boyutun Eleman sayısı Ubound(b,2)-LBound(b,2)+1 6
Dizideki Eleman sayısı 1. boyut elaman sayısı*2.boyut eleman sayısı 66

DIKKAT:Bir dizinin boyutu baştan birkez belirtilirse bir daha asla değişmez. Boyutu değişen dizilere ise aşağıda değineceğiz.

Elemanlara değer atama
İlk değer atama

Dizi elamanlarına index numaralarıyla ulaşırız. Normalde bunlara tek tek değer atamak genelde pratikte karşılaşılan bir durum değildir, belki küçük boyutlu dizilerde olabilir ancak genelde bir döngüsel yapı ile bir hücre grubundan değer okuyup onları atamak şeklinde olmaktadır. Aksi halde kodumuz oldukça uzayacaktır.

						
								Dim Segment(3) As String
								Segment(0) = "Bireysel"
								Segment(1) = "Birebir"
								Segment(2) = "Kobi"
								Segment(3) = "Ticari"

							
						

Döngüyle atamaya örnek olarak da şunu verebiliriz;A1:A20 arasındaki bölge kodlarını bölge dizisine atıyoruz.

						
								For i=1 to 20
								   bölge(i)=Cells(i,1).Value2
								Next i

							
						
Değer atanmamış elemanlar ve Erase fonksiyonu

Henüz değer atanmamış dizi elemanları, dizinin tipine göre default değerlerini alırlar. Bunlar;

  • String diziler için sıfır uzunluklu metin, yani ""
  • Nümerik diziler için 0
  • Variant diziler için Empty
  • Object diziler için Nothing

Statik dizilerde elemanların hepsini tek seferde varsayılan değerlerine döndürmek için Erase fonksiyonu kullanılır. Yani dizi elemanlarının taşıdığı değerler boşaltılır. Ancak bunlar hala hafızada yer kaplamaya devam ederler.

						
								Sub erasestatik()
								  Dim segment(2) As String
								  segment(0) = "bireysel"
								  segment(1) = "birebirt"
								  segment(2) = "kobi"

								  Erase segment()

								  Debug.Print segment(1) '"" döndürür
								End Sub

							
						
Array fonksiyonu

Elle tek tek tanımlama yapılması gereken durumlarda, bir diğer eleman tanımlama yöntemi de Array fonksiyonunu kullanmaktır, ki bu sadece elaman değeri atama değil aynı zamanda diziyi tanımlama yöntemidir de. Zira bu şekilde tanımlanan diziler Variant tipte olurlar ve bildiğiniz gibi Dim ile tanımlanmayan tüm değişkenler Variant kabul edilirler. Bu sayede elemanlar tanımlanırken dizi de yaratılmış olur. Ama biz yine de iyi bir programcı olup dizimizi Dim ile tanımlayalım. Bu yöntemle tanımlanan dizilerde başlangıç indeksi her zaman 0 olur. Yine, bu yöntemle yaratılan diziler 1 boyutlu olur

						
								Dim Segment As Variant
								Segment = Array("Bireysel","Birebir","Kobi","Ticari") 'tek satırda tanımlama imkanı
								Debug.Print Segment(2) 'Kobi yazar

							
						

Bu yöntemin pratik bir kullanımı "ayisim" gibi bir fonksiyon şeklinde kendini gösterebilir. Fonksiyonlarla kafanızı karıştırmamak için şimdilik Sub prosedür şeklinde örneğe görelim.

						
								Sub ayisimornek()
								  Dim ayisim As Variant
								  ayisim = Array("", "Ocak", "Şubat") 'ilk ayı boş geçtim, index 0 olduğu için
								  Debug.Print ayisim(2)
								End Sub

							
						
Dizi içinde dolaşma ve elemanlara erişim

Dizilere eleman atama işini döngülerle yaptığımız gibi, dizi elamanlarını okumayı da yine genelde döngülerle yaparız. Ender olarak kod içinde bir yerde bir index numarası temin edip onu doğrudan da kullandığımız da olur.

						
								For i=1 to 20
								   Cells(i,1).Value2=bölge(i)
								Next i

							
						

Daha şık şekli aşağıdaki gibi olabilir, hatta sadece şık değil aynı zamanda güvenlidir de.

						
								For i=LBound(bölge) to UBound(bölge)
								   Cells(i,1).Value2=bölge(i)
								Next i

							
						

Evet, biraz daha fazla kod yazmış olduk ama güvenlik ön planda olacaksa hard-code(rakamı doğrudan belirterek) alt-üst limit vermek yerine bu fonksiyonlarla vermek daha verimlidir. Hard-code yazıldığında, bölge sayısı 1 arttığında 20 yerine 21 yapmayı unutursanız kodunuz yine hata almadan çalışır ancak eksik çalışmış olur, ve belki akabinde çalışan kodlarla bölgelere otomatik mail gidiyorsa, son bölgeye hiç mail gitmemiş olur. Bir de içiçe bir dizi varsa ve her seviye için üst limit değişiyorsa Ubound'ı kullanmak zaten zaruri olacaktır.
Üstelik bu yazım şekli alt limitin 0 mı 1 mi olduğunu da pek önemsemez . Böylece Option Base var mıydı yok muydu, diziyi (1 to 20) şeklinde mi yoksa (20) şeklinde mi tanımladığınızı hatırlamak zorunda kalmazsınız.
DIKKAT:Dizi içinde For döngüsü ile dolaşırken diziye eleman atama işlemi sadece For i=1 to 10 şeklindeki basit For döngüsü ile yapılabilirken, eleman değerini okuma işlemi ise hem basit For ile hem de For Each ile yapılabilir.
Mesela aşağıdaki gibi bir kullanım sorunsuz çalışırken,

						
								Dim Bölge(1 to 20) As String
								For i=1 to 20
								   bölge(i)=Cells(i,1).Value2
								Next i

							
						

bu kod da hata vermeden çalışır ancak eleman değerlerinin değişmediğini görebilirsiniz. (Eğer daha önce bir değer atanmadıysa içleri boş görünecektir.)

						
								Dim Bölge(1 To 20) As String

								For Each b In Bölge
								   b = ActiveCell.Value2
								   ActiveCell.Offset(1, 0).Select
								Next b

							
						

Yukarıdaki örnekler hep statik dizi örnekleriydi (Array fonksiyonu ile tanımlananlar hariç). Dizimizin boyutunu baştan bilmiyorsak ve kodun gidişatına göre değişken bir şekilde karşımıza çıkma durumu varsa, diziyi boyutsuz yani dinamik tanımlarız ve zamanı geldiğinde boyutunu belirtiriz. Bunu da ReDim ifadesi ile yaparız. Aslında yaptığımız şey, yeni bir statik dizi yaratmaktır. Zira ReDim kullanıp da yeni boyutunu verdiğimiz diziyi yine yeterli bulmazsak tekrar boyut değiştirebiliriz.

						
								Dim şubeler() As String
								....
								....
								ReDim şubeler(800)
							
						

Statik dizilerde, genelde kaynak israfı söz konusudur, zira baştan olası en yüksek değere göre boyut belirlenir ve bu boyutların hepsi çoğu zaman kullanılmaz. Tabii ki bölge sayısı gibi kesin olarak bilinen ve hepsi kullanılan durumlar istisnadır. Bu nedenle genel olarak statik değil dinamik dizi kullanılması önerilir.

Erase Fonksiyonu

Dinamik dizilerde Erase fonksiyonu statik dizilerden farklı çalışır. Statik dizilerde Erase, elemanları default değerlerine atarken dinamik dizide diziyi boyutsuzlaştırır, yani bir nevi siler. Diziyi tekrar kullanmak isterseniz ReDim ile yeniden boyutlandırmanız gerekir.

Diyelim ki ReDim ile 10 elemanlık bir boyutlandırma yaptınız ancak öyle bir nokta geldi ki, 10 eleman yetmiyor, yani boyut artırmanız lazım, böyle bir durumda ReDim Preserve ifadesini kullanırız. Preserve demezsek dizi yine yeni boyuta göre boyutlanır ancak önceki elemanlar silinmiş olur. Preserve ile ilk 10 elemanın değerini de korumuş oluruz.

Boyut artırma durumlarında ReDim'in bir veya iki kez kullanılması önerilir. Birkaç kez boyut artırma ihtiyacı oluyorsa belki dizi değil de Collection kullanmak faydalı olabilir.

NOT: Variant olarak tanımlanan değişkenler doğası gereği dinamiktirler. (Variant tanımlanan diziler ise statiktir)

						
								Dim statikVar As Variant 'standart Variant değişken
								Dim dinamikVar(5) As Variant 'Variant tipli dizi
							
						

Variant Değişken vs Variant Dizi

Variantlarla ilgili detaylı bir örnek aşağıda bulunmaktadır. Bunun oldukça aydınlatıcı olduğunu düşünüyorum.

						
								Sub variantlı()
									Dim aylar1 As Variant 'içine istediğiniz tipte değer atayabileceğiniz bir DEĞİŞKENDİR, buna dizi de dahildir,
									'ama dizi atayana kadar dizi değildir ve IsArray testi false döner. Ubound da kullanamazsınız
									Dim aylar2() As Variant 'Parantez olduğu için bu kesinlikle DİZİDİR, IsArray true döner

									'***öncelikle parantezsiz olan yani değişken olan Aylara bakalım*****
									aylar1 = 1 'number atandı
									Debug.Print aylar1
									'Debug.Print aylar1(1) 'hata verir, çünkü içeriği henüz bir dizi değil.
									Debug.Print IsArray(aylar1) 'false
									'Debug.Print UBound(aylar1) 'çalışmaz çünkü şuan için dizi değil
									aylar1 = "volki" 'string
									Debug.Print IsArray(aylar1) 'yine false
									Debug.Print aylar1
									aylar1 = Now 'tarih
									Debug.Print IsArray(aylar1) 'hala false
									Debug.Print aylar1
									aylar1 = Array("hi", "world", "naber") 'dizi
									Debug.Print IsArray(aylar1) 'artık true, çünkü Array function ile array yaptık
									Debug.Print UBound(aylar1)
									Debug.Print aylar1(2) 'artık hata vermez, çünkü içeriği bir dizi
									aylar1 = Array(Array("hi", "world", "naber"), Array(1, "ss", 3)) 'dizi dizisi
									Debug.Print UBound(aylar1)
									Debug.Print aylar1(1)(1)
									aylar1 = Range("a1:a5").Value ' range atadık, 2 boyutlu dizi
									Debug.Print UBound(aylar1) '5 döner
									Debug.Print aylar1(2, 1) '2.satırdaki değer döner

									'***şimdi de Dizi olan Aylara bakalım
									Debug.Print IsArray(aylar2)
									'Debug.Print UBound(aylar2) hata verir, zira henüz boyut belli değil, ya ReDim yapılmalı ya da Array ile değer atanmalı
									ReDim aylar2(3)
									Debug.Print UBound(aylar2)
									aylar2 = Array("OCAK", "ŞUBAT", "MART", "NİSAN", "MAYIS", "HAZİRAN", "TEMMUZ", "AĞUSTOS", "EYLÜL", "EKİM", "KASIM", "ARALIK")
									Debug.Print UBound(aylar2)
									Debug.Print aylar2(2)
									Debug.Print UBound(aylar2)
									'şimdi de farklı data tiplerinde atama yapıyoruz, Variant olduğu için sorun olmuyor
									aylar2 = Array("OCAK", 2, 3, "30.04.2015", Now + 60, "HAZİRAN", "TEMMUZ", "AĞUSTOS", "EYLÜL", "EKİM", "KASIM", "ARALIK")
									Debug.Print aylar2(4)
								End Sub
							
						

Şimdiye kadar gördüğümüz dizilerin çoğu tek boyutlu idi, ama ihtiyacımıza göre birden fazla boyutlu diziler de oluşturabiliyoruz. Pratikte üçten fazla boyutun kullanıldığını ben açıkçası ne duydum ne de gördüm. Şahsen kendi kodlarımda kullandığım dizilerin büyük kısmı tek boyutludur, birkaç tane 2 boyutlu bir tane de 3 boyutlu dizim bulunmakta. 3 boyutlu dizi örneğinde aynı zamanda Dictionary de kullandığım için bu örneği o bölümde ele alacağız.

Şimdi 2 boyutlu bir dizi kullanım örneğine bakalım. Excel sayfalarının kendileri zaten satır ve sütunlardan oluşmakta olup 2 boyutludurlar ve bu durumu 2 boyutlu dizilerle birlikte çok sık kullanıyor olacağız.

Mesela 10 bölgesi olan bir bankada her bölgenin bir Bireysel bir de Ticari müdürü olduğunu düşünelim. Bunları bir diziye atama işlemi nasıl oluyor ona bakalım.

post-thumb

İngilizcede Array of Array ve Jagged Array olarak kullanılan bu dizi türünü Variant tipte dizi tanımlayarak elde ederiz. Bilindiği gibi Variant veri tipi içinde her şeyi tutabilir, buna diziler de dahildir. Ancak buradaki ayrıma dikkat etmek lazım, klasik Variant bir değişken tanımlamıyoruz, Variant tipli bir dizi tanımlıyoruz. Aradaki ayrımı görmek için şu iki satıra bakalım:

						
								Dim d As Variant 'bu Variant tipli klasik bir değişkendir
								Dim d() As Variant 'bu ise Variant tipli bir dizidir
							
						

İçiçe dizilerin tanımlaması iki ayrı dizi şeklinde olur, ama kullanımı dizi(x)(y) şeklindedir. Yani aslında bu dizi türü tek boyutludur ama içerdiği her eleman da bir başka dizidir.

						
								Dim dışdizi() As Variant 'ana dizi Variant olmalıdır
								Dim içdizi() As String ' Alt dizinin Variant olması gerekmez

								'iç diziyi dış diziye eleman olarak atama
								dışdizi(n) = içdizi
								'nihai kullanım şekli
								Debug.Print dışdizi(x)(y)
							
						

Bu dizilerin kullanımı çok boyutlu dizilere benzer ancak, kullanım ihtiyacı ve şekli küçük farklar göstermektedir. Şöyle ki, çok boyutlu dizilerde tüm boyutlar için hafızada yer ayrılır, belki o boyutlardan bazısı hiç kullanılmayacak bile. Diyelim 20 bölgeli bir banka var, bazı bölgelerde 40 şube varken bazısında 15 şube bulunabilir. Şimdi Şubeler(1 to 20, 1 to 40) şeklinde 2 boyutlu bir dizi tanımlarsak bazı bölgelerde bazı boyutlar israf olacak. İşte içiçe dizilerde bu israf olmuyor. Örneğin 2. bölgenin şube sayısı 18 ise, buradaki 18. şubeyi tanımlayacak son eleman bölge(2)(18) oluyor, bölge(2)(19) şeklinde bir tanımlama yapılmıyor.

Mesela aşağıdaki tabloya göre içiçe dizimizi oluşturalım.

post-thumb

İngilizcede Array of Array ve Jagged Array olarak kullanılan bu dizi türünü Variant tipte dizi tanımlayarak elde ederiz. Bilindiği gibi Variant veri tipi içinde her şeyi tutabilir, buna diziler de dahildir. Ancak buradaki ayrıma dikkat etmek lazım, klasik Variant bir değişken tanımlamıyoruz, Variant tipli bir dizi tanımlıyoruz. Aradaki ayrımı görmek için şu iki satıra bakalım:

						
								Dim d As Variant 'bu Variant tipli klasik bir değişkendir
								Dim d() As Variant 'bu ise Variant tipli bir dizidir
							
						

İçiçe dizilerin tanımlaması iki ayrı dizi şeklinde olur, ama kullanımı dizi(x)(y) şeklindedir. Yani aslında bu dizi türü tek boyutludur ama içerdiği her eleman da bir başka dizidir.

						
								Dim dışdizi() As Variant 'ana dizi Variant olmalıdır
								Dim içdizi() As String ' Alt dizinin Variant olması gerekmez

								'iç diziyi dış diziye eleman olarak atama
								dışdizi(n) = içdizi
								'nihai kullanım şekli
								Debug.Print dışdizi(x)(y)
							
						

Bu dizilerin kullanımı çok boyutlu dizilere benzer ancak, kullanım ihtiyacı ve şekli küçük farklar göstermektedir. Şöyle ki, çok boyutlu dizilerde tüm boyutlar için hafızada yer ayrılır, belki o boyutlardan bazısı hiç kullanılmayacak bile. Diyelim 20 bölgeli bir banka var, bazı bölgelerde 40 şube varken bazısında 15 şube bulunabilir. Şimdi Şubeler(1 to 20, 1 to 40) şeklinde 2 boyutlu bir dizi tanımlarsak bazı bölgelerde bazı boyutlar israf olacak. İşte içiçe dizilerde bu israf olmuyor. Örneğin 2. bölgenin şube sayısı 18 ise, buradaki 18. şubeyi tanımlayacak son eleman bölge(2)(18) oluyor, bölge(2)(19) şeklinde bir tanımlama yapılmıyor.

Mesela aşağıdaki tabloya göre içiçe dizimizi oluşturalım.

						
								Sub içiçedizi()
									Dim bolge(1 To 3) As Variant 'dış dizi 
									Dim subeler() As String 'iç dizi
									Dim i As Integer, s As Integer, k As Integer

									For i = 1 To 3
										ReDim subeler(1 To Cells(i + 1, 2)) 'iç dizinin boyutunu ayarlıyoruz, her bölgede bu boyut değişecek

										'iç diziyi oluşturalım
										For s = 1 To UBound(subeler)
											subeler(s) = Cells(s + k + 1, 6)        
										Next s

										k = k + s - 1 'satır sayısı resetlenmesin diye geçici bir değişkene atıyorum    

										'iç diziyi dış dizinin ilgili elemanına atayalım
										bolge(i) = subeler

									Next i

									Debug.Print bolge(1)(5) 'Şube5
									Debug.Print bolge(2)(2) 'Şube8
									Debug.Print bolge(3)(4) 'hata verir, çünkü 3.bölgenin 4.şubesi yok
								End Sub
							
						

Dizi dizisiyle yapılabilen işlemler "Collection of Collection" veya "Dictionary of Dictionary" yapılarıyla da yapılabilir. Bunları bir sonraki bölümde görüyor olacağız. Bu arada hepsinin avantaj ve dezavantajını anlatan güzel bir sayfa var, ona buradan ulaşabilirsiniz.

Bir hücre grubunu diziye atama (Sayfadan okuma) Dizileri, bir hücre grubundan hızlı veri okuma ve yazma amacıyla da kullanırız. Bu kullanım şekli, özellikle ilgili veri kümesi üzerinde bir güncelleme (belli bir rakamla çarpmak gibi) yapmak istediğinizde idealdir.

Tabi bu amaçla kullanmak istediğimizde standart dizi tanımı yerine Variant olarak tanımlarız, ve tanımladığımız dizi 2 boyutlu bir dizi olur. Zira bir sayfaya baktığınızda gördüğünüz şey satır ve sütunlardan oluşan iki boyutlu bir dizidir. Böyle bir dizide de ilk boyut satır ikinci boyut sütun olur. Bu noktada karışıklığa neden olan bir konu vardır ki o da şudur: Söz konusu hücre grubu tek kolonluk bir alandan oluşuyorsa bile iki boyutludur. İlk boyut satır sayısı kadar elemanlı, ikinci grup da sütun sayısı olan 1 elemanlıdır. Bu tür dizilerde indeks 0'dan değil 1'den başlar. (Tek boyutlu variant dizilerde ise 0'dan başlıyordu)

Mesela aşağıdaki kod ile A1:A1000 hücrelerindeki değerler 100 ile çarpılır.

Bir hücre grubunu diziye atama (Sayfadan okuma) Dizileri, bir hücre grubundan hızlı veri okuma ve yazma amacıyla da kullanırız. Bu kullanım şekli, özellikle ilgili veri kümesi üzerinde bir güncelleme (belli bir rakamla çarpmak gibi) yapmak istediğinizde idealdir.

Tabi bu amaçla kullanmak istediğimizde standart dizi tanımı yerine Variant olarak tanımlarız, ve tanımladığımız dizi 2 boyutlu bir dizi olur. Zira bir sayfaya baktığınızda gördüğünüz şey satır ve sütunlardan oluşan iki boyutlu bir dizidir. Böyle bir dizide de ilk boyut satır ikinci boyut sütun olur. Bu noktada karışıklığa neden olan bir konu vardır ki o da şudur: Söz konusu hücre grubu tek kolonluk bir alandan oluşuyorsa bile iki boyutludur. İlk boyut satır sayısı kadar elemanlı, ikinci grup da sütun sayısı olan 1 elemanlıdır. Bu tür dizilerde indeks 0'dan değil 1'den başlar. (Tek boyutlu variant dizilerde ise 0'dan başlıyordu)

Mesela aşağıdaki kod ile A1:A1000 hücrelerindeki değerler 100 ile çarpılır.

						
								Sub rakamguncelle()
									Dim rakamlar As Variant 'Dizi tanımı
									rakamlar = Range("A1:A10000").Value 'burada hücreden diziye okuma yaptık

									For i = LBound(rakamlar) To UBound(rakamlar) 'i'ler satır boyutudur
										rakamlar(i, 1) = rakamlar(i, 1) / 100 '1 ise sütun boyutu
									Next i

									Range("A1:A10000").Value = rakamlar 'üzerine de yazabiliriz başka bir yere de yazabilirdik
								End Sub
							
						

Alanımız tek kolon değil de birden çok kolondan oluşuyorsa sütun için de bir iç döngü daha eklememiz gerekir. Bu sefer hücre grubumuzu dinamik düşünelim, buna göre kodumuz aşağıdaki gibi olacaktır.

						
								Dim rakamlar As Variant 'Dizi tanımı
								rakamlar = Selection.Value 'burada hücreden diziye okuma yaptık

								For i = LBound(rakamlar) To UBound(rakamlar) 'i'ler satır boyutudur
									For j = LBound(rakamlar, 2) To UBound(rakamlar, 2) 'j'ler sütun boyutudur
										rakamlar(i, j) = rakamlar(i, j) * 1000
									Next j
								Next i

								Selection.Value = rakamlar
							
						

Tek Boyutlu Bir Diziyi Sayfaya Yazdırma

Yazdırma işlemi yapmak için Resize özelliği ile hücreleri yeniden boyutlandırmayı bilmeniz gerekiyor. Resize hakkında bilgi sahibi değilseniz buradan bakıp önbilgi edinebilirsiniz.

Bu işlem için bir Range nesnesi yaratırız ve sonra hedef hücreyi belirleriz. Sonra bu hücreyi Resize ile dizi boyutu kadar genişletiriz.

						
								' Yatay yazma (Tek satırlı çok kolonlu)
								Dim dizi(1 To 3) As Integer
								Dim Hedef As Range

								dizi(1) = 10
								dizi(2) = 20
								dizi(3) = 30

								Set Hedef = Range("A1").Resize(1, UBound(dizi))
								Hedef.Value = dizi
							
						

Eğer dizimizi dikey şekilde yani tek kolon çok satırda yazmak isteseydik, Resize argümanlarını ters çevirmemiz ve Transpose metodunu kullanmamız gerekirdi.

						
								' Dikey yazma (Tek kolon çok satır)
								Dim dizi(1 To 3) As Integer
								Dim Hedef As Range

								dizi(1) = 10
								dizi(2) = 20
								dizi(3) = 30

								Set Hedef = Range("A1").Resize(UBound(dizi), 1)
								Hedef.Value = WorksheetFunction.Transpose(dizi)
							
						

Dizileri Dizilere Atama

Gelişmiş programlama dillerinde olduğunun aksine VBA'de bir diziyi tek seferde başka bir diziye kopyalama/atama yöntemi bulunmamaktadır. Yani şöyle bir kod çalışmaz.

						
								Dim A(1 To 3) As Long
								Dim B(1 To 3) As Long

								A(1) = 100000
								A(2) = 200000
								A(3) = 300000
								B = A
							
						

Peki nasıl yapılır? Tabii ki döngülerle;

						
								Dim A(1 To 3) As Long
								Dim B(1 To 3) As Long

								A(1) = 100000
								A(2) = 200000
								A(3) = 300000

								For i = 1 To 3
									B(i) = A(i)
								Next i

								Debug.Print B(2)
							
						
Dizilerin diğer kullanma şekilleri
Variant Dizileri Variant Dizilere Atama

Dizileri başka dizilere atayamıyoruz ancak içi dizi olan bir Variantı başka bir Varianta atayabiliyoruz. Zira, bu işlem aslında dizi atama değil değişken atamadır.

						
								Dim A As Variant
								Dim B As Variant
								A = Array(100000, 200000, 30000)
								B = A 'atama başarılıdır
								Debug.Print B(2) '30000 yazar
							
						
Range atanmış Variant dizileri normal dizilere atama

Bir hücre grubunu doğrudan bir variant diziye atama şeklini yukarda görmüştük, ama bir şekilde bunları tek boyutlu bir diziye atamanız gerekirse bunu yapmanın yolu döngü kullanmaktır. Tabi istenirse arada hiç variant dizi olmadan da doğrudan ilgili alanlar üzerinden de geçilebilir ancak, eğer bir şekilde ilgili alan kod içinde silindiyse, veya büyük bir alan ise peformans sorunu yaşamamak için Variant diziden normal diziye atamak daha verimli olacaktır, zira diziler üzerindeki işlemler range'lere göre çok daha hızlıdır.
Şimdi aşağıdaki gibi bir hücre grubu olsun, bunu önce Varianta sonra da normal bir diziye atayalım.

post-thumb
						
								Sub cokboyutlu_variant_to_dizi()
									Dim var As Variant
									Dim dizi() As String
									Dim i As Integer, j As Integer

									' B2:D10 hücre aralığındaki değerleri Variant diziye okuma
									var = Range("B2:D10").Value2

									' String dizisini Variant dizinin boyutuna göre yeniden boyutlandırma
									ReDim dizi(1 To UBound(var, 1), 1 To UBound(var, 2))

									' Variant dizisindeki değerleri String dizisine aktarma
									For i = 1 To UBound(var, 1)
										For j = 1 To UBound(var, 2)
											dizi(i, j) = var(i, j)
										Next j
									Next i

									' Dizinin üçüncü satır, ikinci sütun elemanını mesaj kutusunda gösterme
									MsgBox dizi(3, 2)
								End Sub
							
						

Şimdi bunu bir de çok boyutlu dizide yapalım

post-thumb

Çok kolonlu bir Variant diziyi tek boyutlu ve çok boyutlu standart dizilere atama işlemleri aşağıdaki gibidir.

						
								Sub çokkolonlu_variant_to_dizi()
									Dim var As Variant
									Dim dizi() As String
									Dim s As Integer, k As Integer, i As Integer

									var = Range("B2:D10").Value2
									ReDim dizi(1 To UBound(var, 1) * UBound(var, 2))

									s = 0
									For k = 1 To UBound(var, 2)
									   For i = 1 To UBound(var, 1)
										 dizi(i + s) = var(i, k)
									   Next i
									   s = s + UBound(var, 1) 'her boyut geçişinde tekrar başa dönmesin diye aratoplam alınır
									Next k

									MsgBox dizi(15)
								End Sub
							
						

Aynı diziyi iki boyutlu bir standart diziye atamak da aşağıdaki gibi olur.

						
								Sub çokkolonlu_variant_to_çokboyutludizi()
									Dim var As Variant
									Dim dizi() As String
									Dim s As Integer, k As Integer, i As Integer

									var = Range("B2:D10").Value2
									ReDim dizi(1 To UBound(var, 1), 1 To UBound(var, 2))

									For k = 1 To UBound(var, 2)
									  For i = 1 To UBound(var, 1)
										dizi(i, k) = var(i, k)
									  Next i
									Next k

									MsgBox dizi(2, 3)
								End Sub
							
						
Dizileri Bir Prosedüre Parametre Olarak Gönderme

Dizileri bir Sub veya Function prosedüre parametre olarak gönderebiliriz. Unutulmaması gereken nokta, diziler her zaman ByRef anahtar kelimesi ile gönderilir. Aşağıda basit bir örneğimiz bulunuyor.

						
								Sub dizigonder()
									Dim d(5) As String
									'sadece iki elemanı dolduralım
									d(0) = "volkan"
									d(1) = "meltem"

									Call mesajver(d)
								End Sub

								Sub mesajver(ByRef dz() As String)
									For Each a In dz
										If a <> "" Then i = i + 1
									Next a
									eleman = UBound(dz) - LBound(dz) + 1
									MsgBox eleman & " adet elemanın " & i & " tanesi doludur"
								End Sub
							
						
Prosedürden Sonuç Olarak Dizi Döndürme

Fonksiyonlar konusunda göreceğimiz gibi, genelde bir fonksiyondan sadece tek bir değer döndürülür. Aslında bu birçok yerde doğru diye anlatılan yanlış bir bilgidir. Genelde durum böyledir ancak fonksiyonlar tek bir değer döndürmek zorunda değildir, sadece dönüş değeri tek olmak zorundadır. Zira gerek ByRef kullanımı, gerek dönüş değerinin dizi/collection olması sayesinde fonksiyonlar pekala çoklu değer döndürebilirler. Aşağıdaki örnekte çoklu değer olarak dizi döndürüyoruz.

						
								Sub diziata()
									'boyutsuz dizi yaratılır
									Dim d() As Integer

									d = dizidondur '6 değerli bir dizi döner, istediğimizi kullanırız
									Debug.Print d(3)
								End Sub

								Function dizidondur() As Integer() 'dönen değer tipi () ile biter ki dizi döndürdüğü anlaşılsın    
									Dim dizim(0 To 5) As Integer

									For x = 0 To 5
										dizim(x) = 10 * x
									Next x

									dizidondur = dizim
								End Function
							
						
Split ve Join
Split

Dizilerle kullandığımız çok faydalı iki fonksiyon vardır.

Bunlardan ilki Split fonksiyonudur. Bununla, belirli bir ayraçla birleşmiş olan bir String ifade parçalara ayrılarak bir Variant içine veya klasik bir dizi içine aktarılır. Gördüğünüz gibi bu da aslında bir nevi dizi tanımlama yöntemi olmaktadır.

Bu fonksiyon, genelde bir hücreden ";" veya "," ile ayrılmış elemanları tek tek elde etmek için kullanılır.

						
								Sub splitornek()
									Dim s As String
									s = "35516;37770;34234"

									Dim dizi As Variant 'veya Dim dizi() as String şeklinde boyutsuz dizi
									dizi = Split(s, ";")

									Debug.Print dizi(0) '35516 döner
								End Sub
							
						

Bu Split fonksiyonunu kullanarak bir UDF yazmıştım, ki bu sayfayı yazarkenki en yüksek Excel sürümü olan Excel 2016 içinde bile bu fonksiyon hala yerel olarak bulunmuyor. Bunu anlamak gerçekten zor, zira çok ihtiyaç duyulan bir fonksiyon olduğunu düşünüyorum. Neyseki VBA ve UDF var da başımızın çaresine bakabiliriyoruz.

Bu fonksiyon, bir metindeki belirtilen indeksteki kelimeyi seçiyor. Ayracı default olarak " " yani boşluk belirledim, ancak isteyen farklı bir ayraç da belirleyebiliyor. İşte bu fonksiyonum:

						
								Function kelimesec(hucre As Range, kaçıncı As Byte, Optional ayrac As String = " ")
									'normal bir cümlede ayrac boşluk olacağı için ayracı girmeye gerek yok, zaten default olarak " " atadım.
									'ama içeriği mesela / ile ayrılmış bir hücre varsa 3.parametreyi / olarak girersiniz
									Dim kelimeler As Variant
									kelimeler = Split(hucre.Value2, ayrac)
									kelimesec = kelimeler(kaçıncı - 1)
								End Function
							
						
Join

Diğer faydalı fonksiyon da Join olup bir dizi içindeki elemanları, belirtilen ayraçla birleştirip bir String ifade döndürür, yani Splitin tersi gibi çalışır.

						
								Sub birkolonlu_variant_to_dizi()
									Dim var As Variant
									Dim dizi() As String
									Dim kombine As String
									Dim i As Integer

									var = Range("B2:B10").Value2
									ReDim dizi(1 To UBound(var, 1))

									For i = 1 To UBound(var, 1)
										dizi(i) = var(i, 1)
									Next i

									kombine = Join(dizi, ", ")

									MsgBox kombine
								End Sub
							
						

2 Kolonlularda

						
								Sub çokkolonlu_variant_to_dizi()
									Dim var As Variant
									Dim dizi() As String
									Dim kombine As String
									Dim s As Integer, k As Integer, i As Integer

									var = Range("B2:D10").Value2
									ReDim dizi(1 To UBound(var, 1) * UBound(var, 2))

									s = 0
									For k = 1 To UBound(var, 2)
									   For i = 1 To UBound(var, 1)
										 dizi(i + s) = var(i, k)
									   Next i
									   s = s + UBound(var, 1) 'her boyut geçişinde tekrar başa dönmesin diye aratoplam alınır
									Next k

									kombine = Join(dizi, ", ")
									MsgBox kombine
								End Sub
							
						

2. Yöntem: Transpoze (Sadece Tek Kolonsa)

						
								kombine = Join(WorksheetFunction.Transpose(Range("B2:B10").Value), ", ")
							
						

Dizilerle ilgili sık yapılan işlemlerden biri de, bir elemanın o dizide bulunup bulunmadığı ve/veya bulunuyorsa da index numarasını öğrenmek olacaktır.

Filtreleme

Bu işlemlere geçmeden önce Filter fonksiyonundan bahsedelim. Bu metod ile String tipli bir dizinin içinde belirli bir metni arar ve bunları filtreler, eşleşmeyenleri hariç tutarız. (Burada filtrelemekten kastım, elemek değil seçmektir.)

Syntax: Filter(dizi, aranan, [Dahilmihariçmi], [aramatipi])

dizi olarak tek boyutlu bir diziyi parametre olarak veririz, bu nedenle range atanmış bir Variant dizi parametre olarak verilemez.

Dahilmihariçmi parametresi girilmez veya True girilirse aranan kelimeyi içeren kelimeler aranır, False girilirse aranan kelimeyi içermeyen elemanlar filtrelenir.

aramatipi girilmez veya vbBinaryCompare girilirse küçük büyük harf duyarlılığı gözetilerek arama yapılır, vbTextCompare girilirse küçük büyük harf duyarlılığı olmadan arama yapılır. İstisna: ş ve Ş, ç ve Ç v.s aynı kabul edilirken bir tek İ ve i farklı algılanır.

Bununla beraber bu metodu, aranan metnin tam eşleşme durumu için kullanamazsınız. Yani "is" metnini aradığınızda "is, istanbul, mistik" kelimelerinin tamamı gelir.

Örnek 1: Basit Filtreleme
						
								Sub basitfiltre()
									Dim isimler As Variant
									Dim filtreli As Variant

									isimler = Array("volkan", "erhan", "hakan", "özkan", "meltem", "serkan")
									filtreli = Filter(isimler, "kan", True, vbTextCompare)

									For Each f In filtreli
									   Debug.Print f
									Next f
								End Sub
							
						
Örnek 2: İki Boyutlu Dizide Filtreleme

Aşağıdaki örnekte ise içinde "ankara" geçen tüm kelimeler aranır ve filtrelenir. Ancak bu sefer kaynak dizimiz 2 boyutlu ve önce onu tek boyutlu diziye çevirmemiz gerekiyor.

						
								Sub ikiboyutlu_filtre()
									Dim var As Variant
									Dim tekboyutlu() As String
									Dim filtreli As Variant
									Dim i As Integer, j As Integer, k As Integer

									var = Range("A1:C10").Value2
									ReDim tekboyutlu(1 To UBound(var, 1) * UBound(var, 2))

									k = 1
									For i = 1 To UBound(var, 1)
										For j = 1 To UBound(var, 2)
											tekboyutlu(k) = var(i, j)
											k = k + 1
										Next j
									Next i

									filtreli = Filter(tekboyutlu, "ankara", True, vbTextCompare)

									For Each f In filtreli
									   Debug.Print f
									Next f
								End Sub
							
						

Dizilerle ilgili sık yapılan işlemlerden biri de, bir elemanın o dizide bulunup bulunmadığı ve/veya bulunuyorsa da index numarasını öğrenmek olacaktır.

Filtreleme

Bu işlemlere geçmeden önce Filter fonksiyonundan bahsedelim. Bu metod ile String tipli bir dizinin içinde belirli bir metni arar ve bunları filtreler, eşleşmeyenleri hariç tutarız. (Burada filtrelemekten kastım, elemek değil seçmektir.)

Syntax: Filter(dizi, aranan, [Dahilmihariçmi], [aramatipi])

dizi olarak tek boyutlu bir diziyi parametre olarak veririz, bu nedenle range atanmış bir Variant dizi parametre olarak verilemez.

Dahilmihariçmi parametresi girilmez veya True girilirse aranan kelimeyi içeren kelimeler aranır, False girilirse aranan kelimeyi içermeyen elemanlar filtrelenir.

aramatipi girilmez veya vbBinaryCompare girilirse küçük büyük harf duyarlılığı gözetilerek arama yapılır, vbTextCompare girilirse küçük büyük harf duyarlılığı olmadan arama yapılır. İstisna: ş ve Ş, ç ve Ç v.s aynı kabul edilirken bir tek İ ve i farklı algılanır.

Bununla beraber bu metodu, aranan metnin tam eşleşme durumu için kullanamazsınız. Yani "is" metnini aradığınızda "is, istanbul, mistik" kelimelerinin tamamı gelir.

Örnek 1: Basit Filtreleme

						
								Sub filtreornek()
									Dim şubeler As Variant
									Dim filtreli As Variant
									Dim geçici() As String
									Dim i As Integer

									'çeşitli şubelerin olduğu alandan okuma yapılır (sırayla ankara, antalya, ankara, istanbul, sivas yazsın)
									şubeler = Range("A1:A5").Value

									'Variant ve 2 boyutlu olan şubeler dizisi klasik bir boyutlu bir diziye atanır
									ReDim geçici(1 To UBound(şubeler, 1))
									For i = 1 To UBound(şubeler, 1)
									  geçici(i) = şubeler(i, 1)
									Next i

									filtreli = Filter(geçici, "ankara", True, vbTextCompare)

									Set Hedef = Range("C1").Resize(UBound(filtreli) + 1, 1)
									Hedef.Value = filtreli
								End Sub
							
						

Bir Metin Bir Dizide Var mı Kontrolü?

Filter fonksiyonunu öğrendiğimize göre şimdi bunu daha spesifik bir amaçla kullanabiliriz: Bir metnin bir dizide olup olmadığını öğrenmek için. Bunun için birkaç yöntem bulunuyor.

Yöntem 1: Filter Kullanarak

İlk yöntemde diziye filtre uygular ve elde ettiğimiz yeni dizinin üst limitine bakarız. Eğer üst limit 0 veya daha üstü bir sayı ise aradığımız metin dizide var demektir, -1 ise dizide bulunmuyor demektir.

						
								Sub varmı1sub()
									'1.Yöntem: Filter, tam eşleşme aranmıyor
									Dim şubeler As Variant
									Dim filtreli As Variant
									Dim geçici() As String
									Dim i As Integer

									'çeşitli şubelerin olduğu alandan okuma yapılır (sırayla ankara, antalya, ankara, istanbul, sivas yazsın)
									şubeler = Range("A1:A5").Value

									'Variant ve 2 boyutlu olan şubeler dizisi klasik bir boyutlu bir diziye atanır
									ReDim geçici(1 To UBound(şubeler, 1))
									For i = 1 To UBound(şubeler, 1)
									   geçici(i) = şubeler(i, 1)
									Next i

									filtreli = Filter(geçici, "ankara", True, vbTextCompare)

									If UBound(filtreli) = -1 Then
									  MsgBox "aradığınız kelime dizide yok"
									Else
									  MsgBox "aradığınız kelime dizide var"
									End If
								End Sub
							
						

Yöntem 2: Fonksiyon Haline Getirme

Bunu bir fonksiyon haline getirmek daha kullanışlı yapacaktır.

						
								'1'in function hali
								Function Varmı1(aranan As String, dizi As Variant) As Boolean
									Varmı1 = (UBound(Filter(dizi, aranan, True, vbTextCompare)) > -1)
								End Function

								'--------
								Sub test1()
									isimler = Array("volkan", "erhan", "hakan", "özkan", "meltem", "serkan")
									Debug.Print Varmı1("volk", isimler) 'True
									Debug.Print Varmı1("volkan", isimler) 'True
								End Sub
							
						

Eğer tam eşleşme istiyorsak, Match fonksiyonunu kullanmalıyız veya Filter dışında çözüm sağlayan 2. yöntemi denemeliyiz. Bu yöntemde 1. yöntemin aksine sayısal değerleri de arayabiliyoruz. Bu yüzden hem sayıları hem stringleri kapsayacak şekilde Variant tipte bir aranan parametresi belirtiyoruz.

Yöntem 2: Match Fonksiyonu Kullanarak

						
								Function Varmı2(aranan As Variant, dizi As Variant) As Boolean
									'2.yöntem) Match, tam eşleşme sağlanıyor
									On Error GoTo hata
										Varmı2 = Not IsError(WorksheetFunction.Match(aranan, dizi, 0))
									Exit Function
								hata:
									Varmı2 = False
								End Function

								'-----------
								Sub test2()
									isimler = Array("volkan", "erhan", "hakan", "özkan", "meltem", "serkan")
									Debug.Print Varmı2("volk", isimler) 'False
									Debug.Print Varmı2("volkan", isimler) 'True

									sayılar = Array(1, 2, 3)
									Debug.Print Varmı2(1, sayılar) 'True
									Debug.Print Varmı2(10, sayılar) 'False
								End Sub
							
						

Yöntem 3: Join ve InStr Fonksiyonları Kullanarak

3. yöntem, dizi elemanlarını Join ile birleştirip, yine bir String fonksiyonu olan ve bir string içinde bir alt metin arayan InStr fonksiyonu ile arama yapmak şeklinde olabilir, üstelik bu yöntem tam eşleşme sağlar. Bunda da 2. yöntemde olduğu gibi sayısal değerleri de arayabiliyoruz, o yüzden fonksiyonumuzu Variant tanımlıyoruz.

						
								Function varmı3(aranan As Variant, dizi As Variant) As Boolean
									ayraç = "-"
									joinli = ayraç & Join(dizi, ayraç) 'tam eşleşmeyi sağlamak için başına da ilgili ayracı koyuyoruz
									If InStr(1, joinli, ayraç & aranan & ayraç, vbTextCompare) Then
										varmı3 = True
									Else
										varmı3 = False
									End If
								End Function

								Sub test3()
									isimler = Array("volkan", "erhan", "hakan", "özkan", "meltem", "serkan")
									Debug.Print varmı3("volk", isimler) 'False
									Debug.Print varmı3("volkan", isimler) 'True

									sayılar = Array(1, 2, 3)
									Debug.Print varmı3(1, sayılar) 'True
									Debug.Print varmı3(10, sayılar) 'False
								End Sub
							
						
Yöntem 4: Dictionary Kullanarak

Daha basit bir yol olan Dictionary kullanılabilir, tabi kodun genel yapısı buna uygun ise. Bunu sonraki sayfalarda göreceğiz.

Yöntem 5: Arama Algoritmaları Kullanarak

Arama algoritmalarından birini kullanarak kendi fonksiyonunuzu yazmayı önerebilirim ama çok profesyonelleşmek istemediğiniz sürece bu sulara girmenize gerek yoktur.

Var mı, varsa kaçta?

Yukarıdaki Match fonksiyonunu kullandığımız 2. yöntemde aslında ilgili metnin kaçıncı sırada olduğunu buldurmaya yarayan bir fonksiyon kullanmış olduk. Eğer bulamazsa hata döndürür, zaten biz de hata döndürüp döndürmediğine bakıyorduk, döndürmüyorsa elemanı içeriyor diyorduk. Peki ya hata döndürmüyorsa? Döndürdüğü şey sıra numarası olmaktadır. Dizileri sıralama Algoritma kavramına detaylı girdiğimizde karşımıza çıkan en temel algoritmalardan biri de sıralama algoritmalarıdır.(Diğeri de hemen az önce bahsettiğim arama algoritmasıdır). Bu sıralama algoritmalarını anlamak önemli, zira dizilerle ilgili olarak sıralama yapmayı sağlayan ne yerleşik bir fonksiyon/metod, ne de arama yaparken kullandığımız gibi yardımcı yöntemler(Match, Join) bulunmaktadır. Kendi fonksiyonumuz kendimizin yazması gerekmektedir. Bunların da kendi içinde türleri vardır. Bubble Sort, Merge Sort, Quick Sort gibi. Burada bunlardan ikisine bakacağız. Şu ve şu sitelerde daha bol miktarda örnek bulunmakta, şu ve şu(Türkçe) sitede ise muazzam bir kaynak bulunmaktadır. Bu son sitelerden farklı yönteleri öğrenip VBA kodlamasını yapmayı deneyebilirsiniz. Bubble Sort Bu yöntem en basit yöntem olup, aynı zamanda en yavaş çalışan yöntemdir de. Gerçi küçük dizilerde bu yavaşlık problemi çok farkedilmez. Çalışma yöntemi, tekrarlı bir şeklide komşu değerlerin sıralamasını yapmaktan ibarettir. Aşağıdaki örnekte tüm aşamaların üzerinden tek tek geçeceğiz. Elimizdeki dizi 5,1,4,2,8 rakamlarından oluşsun.

1. Aşama
Önce Sonra
( 5 1 4 2 8 ) ( 1 5 4 2 8 )
( 1 5 4 2 8 ) ( 1 4 5 2 8 )
( 1 4 5 2 8 ) ( 1 4 2 5 8 )
( 1 4 2 5 8 ) ( 1 4 2 5 8 ) 'burada bir değişiklik olmaz, sırası doğru
2. Aşama
Önce Sonra
( 1 4 2 5 8 ) ( 1 4 2 5 8 )
( 1 4 2 5 8 ) ( 1 2 4 5 8 )
( 1 2 4 5 8 ) ( 1 2 4 5 8 )
( 1 2 4 5 8 ) ( 1 2 4 5 8 )

Şu anda sıralama tamam ancak bu aşamada 1 tane de olsa bir değişim olduğu için algoritmanın devam etmesi lazım, ta ki hiç değişim olmayana kadar.

3. Aşama
Önce Sonra
( 1 2 4 5 8 ) ( 1 2 4 5 8 )
( 1 2 4 5 8 ) ( 1 2 4 5 8 )
( 1 2 4 5 8 ) ( 1 2 4 5 8 )
( 1 2 4 5 8 ) ( 1 2 4 5 8 )

Evet 3.aşamada artık bir değişiklik yok ve algoritmamız durur. Şimdi bunun kodunu görelim.

						
								Function BubbleSort(arr) As Variant
									Dim geçici As Variant
									Dim i As Long
									Dim j As Long
									Dim lngMin As Long
									Dim lngMax As Long

									lngMin = LBound(arr)
									lngMax = UBound(arr)
									For i = lngMin To lngMax - 1
										For j = i + 1 To lngMax
											If arr(i) > arr(j) Then
												geçici = arr(i)
												arr(i) = arr(j)
												arr(j) = geçici
											End If
										Next j
									Next i

									BubbleSort = arr
								End Function

								'-------
								Sub bubble_sort_test()
									Dim dizi As Variant
									Dim sıralı As Variant

									dizi = Array(5, 1, 4, 2, 8)
									sıralı = BubbleSort(dizi)

									Call Diziyazdır(sıralı)
								End Sub
							
						
QuickSort Algoritması

QuickSort (Hızlı sıralama) algoritması, böl ve fethet felsefesine dayanan bir yöntem uygular. Bu yöntemde, bir referans seçilir ve bu referanstan küçük olanlar sol tarafa, büyük olanlar sağ tarafa konulur. İşte bu algoritmanın VBA kodu:

						
								Function Parçala(ByRef dizi As Variant, ByVal low As Integer, ByVal high As Integer) As Integer
									referans = dizi(high)
									Debug.Print "Referansımız:" & referans & ".Bundan küçükler sola, büyükler sağa. referans ortalarına. Soldakiler önce ele alınacak, sağdakiler daha sonra."
									i = low - 1
									For J = low To high - 1
										If dizi(J) <= referans Then
											i = i + 1
											Debug.Print i, J, dizi(J) & "<= referans olduğu için " & dizi(i) & " ile " & dizi(J) & " yer değiştirecek." & dizi(i) & " yavaşça sağa kayacak: " & Join(dizi, "-")
											geçici = dizi(i)
											dizi(i) = dizi(J)
											dizi(J) = geçici
										Else
											Debug.Print i, J, dizi(J) & ">" & "referans(" & referans & ") olduğu için yer değişme yok"
										End If
									Next J
									Call DiziBitişikyazdır(dizi, "for çıkışında dizinin durumu:")

									Debug.Print dizi(i + 1) & " ile " & dizi(high) & " yer değiştirecek, yani referans ortaya girecek."
									geçici2 = dizi(i + 1)
									dizi(i + 1) = dizi(high)
									dizi(high) = geçici2
									Call DiziBitişikyazdır(dizi, "Bu aşamanın sonunda dizimiz:")
									Debug.Print "-----------------------------------------" & vbNewLine

									Parçala = i + 1
								End Function

								'------------------------------------
								Sub hızlısırala(ByRef dizi As Variant, ByVal low As Integer, ByVal high As Integer)
									If low < high Then
										Pi = Parçala(dizi, low, high)
										hızlısırala dizi, low, Pi - 1
										hızlısırala dizi, Pi + 1, high
									End If
								End Sub

								'------------------------------------
								'bu kodu çalıştırarak test ediyoruz
								Sub quick_sort_test()
									karışıkdizi = Array(5, 1, 4, 2, 8)
									'karışıkdizi = Array(52, 3, 45, 32, 8, 19, 47, 26, 1, 100, 68, 24, 12, 9, 72, 55, 36)
									Call DiziBitişikyazdır(karışıkdizi, "İlk giriş:")
									hızlısırala karışıkdizi, 0, UBound(karışıkdizi)
									Call DiziBitişikyazdır(karışıkdizi, "Nihai sıralama sonucu:")
								End Sub
							
						
Diziler için Utility Modülü

Dizilerle ilgili olarak sık yapılan işlemleri, özellikle test/kontrol için yapılan işlemleri bir modül içinde toparlamak iyi bir fikir olacaktır. Bunlardan sıralamayı yukarıda görmüştük, diğerlerine birkaç örneği ise aşağıda bulabilirsiniz.

						
								Sub Diziyazdır(dizi As Variant)
									For Each d In dizi
										Debug.Print d
									Next d
								End Sub

								'------------------
								Sub DiziBitişikyazdır(dizi As Variant, Optional başlık As String = "Birleşen dizi:")
									bitişik = Join(dizi, "-")
									Debug.Print başlık & bitişik & vbNewLine
								End Sub

								'-------------
								Function ikidizibirleştir(dizi1 As Variant, dizi2 As Variant)
									Dim geçici As Variant

									adet = UBound(dizi1) + UBound(dizi2) + 2
									ReDim geçici(adet - 1)

									For i = LBound(dizi1) To UBound(dizi1)
										geçici(i) = dizi1(i)
									Next i

									For i = UBound(dizi1) + 1 To adet - 1
										geçici(i) = dizi2(i - 1 - UBound(dizi1))
									Next i

									'kontrol için
									DiziBitişikyazdır geçici

									ikidizibirleştir = geçici
								End Function
							
						

Hatırlatma: Sayfanın başında belirttiğim gibi buraya da bakmanızı tavsiye ediyorum.