30.09.2018 tarihinde Excel bölümüne Dış Verilerle çalışmak sayfası eklenmiştir

04.08.2018 tarihinde VBA bölümüne ObjelerDünyası sayfası eklenmiştir

25.07.2018 tarihinde VBA bölümüne Outlook programlama sayfası eklenmiştir

13.07.2018 tarihinde VBA bölümüne Formlar-Kontroller sayfası eklenmiştir

25.05.2018 Hosting şirketi dğeiştirmekten kaynaklı bir hata nedeniyle Excelent add-ini indirirken hata alınmaktaydı. Bu hata düzeltilmiştir. İki ayrı download alternatifi sunulmuştur. Kurumunuzun BT politikalarının veya şahsi PC’nizdeki güvenlik ayarlarının izin vermesi durumunda yöntemlerden biriyle kurulum yapabilmelisiniz. Bi sorun olursa bana iletebilirseniz sevinirim.

VBAMakro Diziler ve Dizimsi Yapılar 1

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.

Temel bilgiler

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

ve

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.

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

İleri seviye işlemler

Dinamik diziler

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özkonusudur, zira baştan olası en yüksek değere göre boyut belirlenir ve bu boyutların hepsi çoğu zaman kullanılmaz. Tabiki 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.

İlave boyut artışı

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ın 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 oolduğ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

'***şmdi 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") ' variant olduğu için içine karışık tipli verilen atayabilirm
Debug.Print aylar2(4)

End Sub

Çok boyutlu diziler

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

Sub ikiboyutludizi()
Dim mudur(1 To 10, 1 To 2) As Long 'ilk boyut bölge için, ikinci boyut segment tipi için
Dim i As Integer, j As Integer

For i = LBound(mudur, 1) To UBound(mudur, 1)
    For j = LBound(mudur, 2) To UBound(mudur, 2)
        mudur(i, j) = Cells(i + 1, j + 1).Value
    Next j
Next i

Debug.Print mudur(3, 2)
End Sub

Tabi bunun için aşağıda başka bir yöntem var ki, bundan çok daha basittir.

İçiçe diziler(Dizi dizileri)

İ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. Yanlız burdaki 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 elaman da bir başka dizidir.

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

'nihai kullanım şekli
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ümboyutlar 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, burdaki 18.şubeyi tanımlayacak son elaman şube(2)(18) oluyor, şube(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.

Rangeler ve Diziler

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 bi 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özkonusu 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 elamanlıdır.

Mesela aş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 yazabilriz başka bir yere de yazabilirdik

End Sub

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 kolonk ç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)

İki boyutlu dizileri sayfaya yazdırma

İki boyutlu bir dizimizi sayfaya yazdırmak istiyorsak yine Resize metodunu kullanırız, ilk boyutu satır olarak, ikinci boyutu da kolon olarak kullanabiliriz, veya tam tersini de istersek yerlerini değiştirip Transpoze yaparız.

Dim dizi(1 To 3, 1 To 2) As Integer
Dim alan As Range

dizi(1, 1) = 10
dizi(2, 1) = 20
dizi(3, 1) = 30
dizi(1, 2) = 100
dizi(2, 2) = 200
dizi(3, 2) = 300

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

Transpoze hali de şöyle:

Dim dizi(1 To 3, 1 To 2) As Integer
Dim alan As Range

dizi(1, 1) = 10
dizi(2, 1) = 20
dizi(3, 1) = 30
dizi(1, 2) = 100
dizi(2, 2) = 200
dizi(3, 2) = 300

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

Dizilerin diğer kullanma şekilleri

Dizileri dizilere atama

Gelişmiş programlama dilllerinde 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? Tabiki 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 10
   B(i)=A(i)
Next i

Debug.Print B(2)

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.

Sub birkolonlu_variant_to_dizi()
Dim var As Variant
Dim dizi() 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

MsgBox dizi(3)
End Sub

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

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

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 collectiona atama

Bazı durumlarda elimizdeki diziyi daha fazla esnekliği olan ve bazı yönlerden dizilere benzeyen collectionlara atamak isteriz. Ancak colletion konusunu henüz işlemediğimiz için(siteyi sırayla okuduğunuz varsayımıyla hareket ederek) bu durumu collection sayfasında ele alacağız.

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.

SSub 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 joinornek()
Dim b As String
Dim v As Variant
v = Array(35516, 34433, 32335)

b = Join(v, ";")
Debug.Print b

End Sub

Bu fonksiyon sadece bir boyutlu dizilerde çalışır. O yüzden bir Range'den okunan ve 2 boyutlu olan bir Variant dizide işe yaramayacaktır. Ama bunu yapmanın da bazı yolları var. Yukarda Variant dizileri klasik dizilere çevirmeyi görmüştük, yine böyle yaparız ve gerisi kolay.

1.Yöntem:Variant diziyi normal diziye dönüştürmek

1 kolonlularda

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), ", ")

Varmı kontrolü ve Index bulma

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.

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 bouytlu 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üçükbü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.

Aşağıdaki örnekte içinde "ankara" geçen tüm kelimeler küçük/büyük harf ayrımı olmadan aranır ve filtrelenir.

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
şubeler = Range("A1:A100").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 = WorksheetFunction.Transpose(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.

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

'1.Yöntem:a)Filter, tam eşleşme aranmıyor
......
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

'duruma göre bu işlemi bir fonksiyona da atayabilirsin
Function Varmı(aranan As String, dizi As Variant) As Boolean
  Varmı = (UBound(Filter(dizi, aranan)) > -1)
End Function

Tabi bu yöntemde tam eşleşme durumu sağlanmıyor, ilgili metni barındıran bir kelime var mı diye kontrol ediyor. Eğer ki tam eşleşme istiyorsak, Match fonksiyonunu kullanmalıyız veya Filter dışında çözüm sağlayan 2.yöntemi denemeliyiz.

'1.Yöntem:b)Filter, tam eşleşme sağlanıyor
Function Varmı(aranan As String, dizi As Variant) As Boolean
    Varmı = Not IsError(WorksheetFunction.Match(aranan, dizi, 0))
End Function

2. yöntem, dizi elemanlarını Join ile birleştirip, yine bir String fonksiyonu olan ve bir string içinde bir altmetin arayan InStr fonksiyonu ile arama yapmak şeklinde olabilir, üstelik bu yöntem az önce belirtildiği gibi tam eşleşme sağlar.

'2.Yöntem:Join ve InStr:tam eşleşme sağlanır
Sub joininstr()
...
ayraç = "-"
joinli = ayraç & Join(geçici, ayraç) 'tam eşleşmeyi sağlamak için başına da ilgili ayracı koyuyoruz

If InStr(1, joinli, ayraç & aranan & ayraç, vbTextCompare) Then
   MsgBox "Aranan metin dizide bulunuyor"
Else
   MsgBox "Aranan metin dizide bulunmuyor"
End If

End Sub

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

Var mı, varsa kaçta

Yukarıdaki Match fonksiyonunu kullandığımız 1b yönteminde 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.

Hatırlatma

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

YORUMLAR