Szótárak (hashtáblák) és szótártömbök

A strukturált adatszerkezetek esetében nagyon praktikusan használható adattípus a hashtable , vagy magyarul talán szótárnak  vagy asszociatív tömbnek  lehetne hívni.

[9] PS C:\> $hash = @{ Név = "Soós Tibor"; Cím = "Budapest"; "e-mail"="soostibo

r@citromail.hu"}

[10] PS C:\> $hash

 

Name                           Value

----                           -----

Név                            Soós Tibor

e-mail                         soostibor@citromail.hu

Cím                            Budapest

A hashtábla jelölése tehát egy kukac-kapcsos zárójelpár (@{} ) jellel és egy lezáró kapcsos zárójellel (}) jellel történik. Belül kulcs=érték párokat kell elhelyezni. A kulcs nevét csak akkor kell idézőjelezni, ha az valami speciális karaktert (pl. szóköz, kötőjel, stb.) tartalmaz. Egyébként a kulcs nem feltétlenül szöveg, akármilyen adattípus lehet, de azért általában szövegeket szoktunk használni. Az érték megadásánál az eddig megszokott formákat kell alkalmazni.

Hogyan tudok vajon egy újabb személyt felvenni ebbe a hashtáblába? Nézzük meg ehhez a hashtábla tagjellemzőit:

[12] PS C:\> $hash | Get-Member | ft -wrap

 

 

   TypeName: System.Collections.Hashtable

 

Name               MemberType            Definition

----               ----------            ----------

Add                Method                System.Void Add(Object key, Object

                                          value)

Clear              Method                System.Void Clear()

Clone              Method                System.Object Clone()

Contains           Method                System.Boolean Contains(Object key

                                         )

ContainsKey        Method                System.Boolean ContainsKey(Object

                                         key)

ContainsValue      Method                System.Boolean ContainsValue(Objec

                                         t value)

CopyTo             Method                System.Void CopyTo(Array array, In

                                         t32 arrayIndex)

Equals             Method                System.Boolean Equals(Object obj)

GetEnumerator      Method                System.Collections.IDictionaryEnum

                                         erator GetEnumerator()

GetHashCode        Method                System.Int32 GetHashCode()

GetObjectData      Method                System.Void GetObjectData(Serializ

                                         ationInfo info, StreamingContext c

                                         ontext)

GetType            Method                System.Type GetType()

get_Count          Method                System.Int32 get_Count()

get_IsFixedSize    Method                System.Boolean get_IsFixedSize()

get_IsReadOnly     Method                System.Boolean get_IsReadOnly()

get_IsSynchronized Method                System.Boolean get_IsSynchronized(

                                         )

get_Item           Method                System.Object get_Item(Object key)

get_Keys           Method                System.Collections.ICollection get

                                         _Keys()

get_SyncRoot       Method                System.Object get_SyncRoot()

get_Values         Method                System.Collections.ICollection get

                                         _Values()

OnDeserialization  Method                System.Void OnDeserialization(Obje

                                         ct sender)

Remove             Method                System.Void Remove(Object key)

set_Item           Method                System.Void set_Item(Object key, O

                                         bject value)

ToString           Method                System.String ToString()

Item               ParameterizedProperty System.Object Item(Object key) {ge

                                         t;set;}

Count              Property              System.Int32 Count {get;}

IsFixedSize        Property              System.Boolean IsFixedSize {get;}

IsReadOnly         Property              System.Boolean IsReadOnly {get;}

IsSynchronized     Property              System.Boolean IsSynchronized {get

                                         ;}

Keys               Property              System.Collections.ICollection Key

                                         s {get;}

SyncRoot           Property              System.Object SyncRoot {get;}

Values             Property              System.Collections.ICollection Val

                                         ues {get;}

Láthatjuk, az Add metódust, így nézzük meg, azzal mit kapunk:

[13] PS C:\> $hash.Add("Név","Fájdalom Csilla")

Exception calling "Add" with "2" argument(s): "Item has already been added. Ke

y in dictionary: 'Név'  Key being added: 'Név'"

At line:1 char:10

+ $hash.Add <<<< ("Név","Fájdalom Csilla")

    + CategoryInfo          : NotSpecified: (:) [], MethodInvocationException

    + FullyQualifiedErrorId : DotNetMethodException

Hát erre bizony hibajelzést kaptam, mert nem szereti ez az adatszerkezet, ha ugyanolyan kulccsal még egy értéket akarok felvenni. Csak megváltoztatni tudom az adott kulcshoz tartozó értéket:

[17] PS C:\> $hash.set_Item("Név","Fájdalom Csilla")

[18] PS C:\> $hash

 

Name                           Value

----                           -----

e-mail                         soostibor@citromail.hu

Cím                            Budapest

Név                            Fájdalom Csilla

 

 

[19] PS C:\> $hash["Név"]="Beléd Márton"

[20] PS C:\> $hash

 

Name                           Value

----                           -----

e-mail                         soostibor@citromail.hu

Cím                            Budapest

Név                            Beléd Márton

 

 

[21] PS C:\> $hash.Név = "Pandacsöki Boborján"

[22] PS C:\> $hash

 

Name                           Value

----                           -----

e-mail                         soostibor@citromail.hu

Cím                            Budapest

Név                            Pandacsöki Boborján

A fenti példában látszik, hogy három különböző szintaxissal is lehet módosítani értékeket ([17], [19] és [21]-es sorok).

Lekérdezni az értékeket is a fenti lehetőségekhez hasonlóan lehet:

[27] PS C:\> $hash.Név

Pandacsöki Boborján

[28] PS C:\> $hash["Név"]

Pandacsöki Boborján

[29] PS C:\> $hash.get_item("Név")

Pandacsöki Boborján

Szóval a lehetőségek elég széles tárháza áll rendelkezésre. Külön lekérhetjük a hashtábla kulcsait és értékeit is:

[32] PS C:\> $hash.keys

e-mail

adat

Cím

Név

[33] PS C:\> $hash.values

soostibor@citromail.hu

12345

Budapest

Pandacsöki Boborján

Ha a hashtáblát a @{} jelekkel hozzuk létre, akkor a kulcsok kis-nagybetű érzéketlen, azaz:

PS C:\> $h = @{}

PS C:\> $h.a = 5

PS C:\> $h.A

5

Tehát a fenti $h.a-t meg lehet kapni a $h.A-val is. Ha kis-nagybetű érzékeny hashtáblát szeretnénk, akkor hozzuk létre a hashtáblát a New-Object cmdlet segítségével így:

PS C:\> $hcs = New-Object -TypeName hashtable

PS C:\> $hcs.a = 10

PS C:\> $hcs.A

PS C:\> $hcs.A = 20

PS C:\> $hcs

 

Name                           Value

----                           -----

A                              20

a                              10

De visszatérve a problémámhoz, hogyan lehet még egy ember adatait berakni ebbe a hashtáblába? Egy hashtáblába sehogy, de nagyon egyszerűen létre tudunk hozni egy hashtábla-tömböt, méghozzá abból a System.Collections.ArrayList fajtából, amit tudunk bővíteni:

[37] PS C:\> $névjegyek = New-Object system.collections.arraylist

[38] PS C:\> $névjegyek.Add(@{Név="Soós Tibor";

"e‑mail"="soostibor@citromail.hu";Cím="Budapest"})

0

[39] PS C:\> $névjegyek.Add(@{Név="Fájdalom Csilla";

"e‑mail"="fcs@citromail.hu";Cím="Zamárdi"})

1

[40] PS C:\> $névjegyek

 

Name                           Value

----                           -----

e-mail                         soostibor@citromail.hu

Cím                            Budapest

Név                            Soós Tibor

e-mail                         fcs@citromail.hu

Cím                            Zamárdi

Név                            Fájdalom Csilla

 

 

[41] PS C:\> $névjegyek.count

2

[42] PS C:\> $névjegyek[0]

 

Name                           Value

----                           -----

e-mail                         soostibor@citromail.hu

Cím                            Budapest

Név                            Soós Tibor

 

 

[43] PS C:\> $névjegyek[1]

 

Name                           Value

----                           -----

e-mail                         fcs@citromail.hu

Cím                            Zamárdi

Név                            Fájdalom Csilla

 

 

[44] PS C:\> $névjegyek[1].Név

Fájdalom Csilla

[45] PS C:\> $névjegyek[1]."e-mail"

fcs@citromail.hu

Így ezzel a hashtábla-tömbbel adatbázis-szerű alkalmazási területek adatleképezési igényeit is nagyon jól ki lehet elégíteni.

Hashtáblák definiálására a 2.0-ás PowerShellben egy új cmdlet is rendelkezésünkre áll, hogy még egyszerűbben definiálhassunk ilyen adattípust, ez pedig a ConvertFrom-StringData :

[58] PS C:\> $hashstring = @"

>> első = Ez itt az első érték

>> második = az érték részt nem is kell idézőjelezni

>> harmadik = soostibor@citromail.hu

>> negyedik = 4

>> ötödik = 2009.11.21

>> "@

>> 

[59] PS C:\> $hash = ConvertFrom-StringData $hashstring

[60] PS C:\> $hash

 

Name                           Value

----                           -----

negyedik                       4

második                        az érték részt nem is kell idézőjelezni

ötödik                         2009.11.21

harmadik                       soostibor@citromail.hu

első                           Ez itt az első érték

 

 

[61] PS C:\> $hash.első

Ez itt az első érték

[62] PS C:\> ($hash.negyedik).gettype().fullname

System.String

[63] PS C:\> ($hash.ötödik).gettype().fullname

System.String

Mint ahogy látható, a hashtábla egy többsoros sztringből képződik. A sztring egyes sorait úgy kell felépíteni, hogy a baloldalon legyen egy kulcsnév, aztán egy egyenlőségjel és jobb oldalon egy érték. Vigyázat, ezen a módon csak szöveges értékeket tudunk a hashtáblába felvenni. Hiába adtam meg számnak és dátumnak kinéző sorokat a negyedik és ötödik kulcsként, ahogy a [62]-es és [63]-as sorokban látszik ezek mind egyszerű szövegként lettek betöltve a hashtáblába.

Fontos megjegyezni, hogy a hashtáblák kiíratásakor az egyes kulcsok nem előre meghatározott módon íródnak ki. Maga a „hash” szó, azaz magyarul „kivonat” onnan jön az adattípus nevében, hogy a belső tárolási módja ezeknek úgy történik, hogy a kulcsból képződik egy kivonat, ami alapján lesznek az adatok ténylegesen tárolva, és ez alapján gyorsan tudja ellenőrizni a .NET keretrendszer, hogy az adott kulcs már létezik-e, illetve az adatok kiolvasásában, a kulcsok megkeresésében is van szerepe.

Alaphelyzetben tehát össze-vissza az elemek sorrendje:

PS C:\> $hash = @{"ab" = 1; "bb" = 2; "cb" = 3; "db" = 4}

PS C:\> $hash

 

Name                           Value

----                           -----

db                             4

cb                             3

ab                             1

bb                             2

Hogyan lehetne ABC rendben kiíratni a hashtábla adatait? Próbálkozzunk a Sort-Object-el direktben:

PS C:\> $hash | Sort-Object -Property Name

 

Name                           Value

----                           -----

db                             4

cb                             3

ab                             1

bb                             2

Nem történt semmi! Ennek magyarázata az, hogy a hashtábla a futószalagon egy egységként, egy darabban vándorol, annak ellenére, hogy látszólag itt is több objektum látszik a konzolon, de ez csak az ellenség megtévesztése:

PS C:\> @($hash).count

1

Ahhoz, hogy a hashtábla elemeit sorba tudjuk rendezni, fel kell darabolni azt. Erre a nagyon hasznos GetEnumerator()  metódus áll rendelkezésre:

PS C:\> @($hash.GetEnumerator()).count

4

Ezzel tényleg soronként 1-1 objektumot kapunk Name és Value tulajdonságokkal és így már könnyedén sorba tudjuk rendezni az elemeket:

PS C:\> $hash.GetEnumerator() | Sort-Object -Property Name

 

Name                           Value

----                           -----

ab                             1

bb                             2

cb                             3

db                             4

De ennek végén már nem hashtábla az objektumunk! Ha mindenképpen fontos a sorrendiség, akkor használhatjuk a .NET keretrendszer egy másik osztályát, a System.Collections.SortedList -et, ami nagyon hasonlít a hashtáblához:

[54] PS C:\> $sl = new-object system.collections.sortedlist

[55] PS C:\> $sl.ab = 1

[56] PS C:\> $sl.bb = 2

[57] PS C:\> $sl.cd = 3

[58] PS C:\> $sl.db = 4

[59] PS C:\> $sl

 

Name                           Value

----                           -----

ab                             1

bb                             2

cd                             3

db                             4

A fő különbség, hogy itt a kulcsok abc rendjében jelennek meg az elemek, sőt, ha beillesztek egy új elemet, akkor azt is természetesen az abc sorrendben jeleníti meg:

[62] PS C:\> $sl.aa = "új elem"

[63] PS C:\> $sl

 

Name                           Value

----                           -----

aa                             új elem

ab                             1

bb                             2

cd                             3

db                             4

A szótár általános adattípusa

Ahogy a típusos tömbnél láttuk az általános (generic) adattípust, úgy a hashtáblának is van ilyenje. Ezzel olyan speciális hashtáblát tudunk létrehozni, melynek akár a kulcs mezőjének, akár az érték mezőjének típusát meg tudjuk szabni.

[14] PS C:\> $genhash = New-Object "collections.generic.dictionary[string,int]"

 

[15] PS C:\> $genhash.elem1 = 1

[16] PS C:\> $genhash.elem2 = "valami"

The value "valami" is not of type "System.Int32" and cannot be used in this ge

neric collection.

Parameter name: value

At line:1 char:10

+ $genhash. <<<< elem2 = "valami"

    + CategoryInfo          : InvalidOperation: (:) [], RuntimeException

    + FullyQualifiedErrorId : PropertyAssignmentException

A fenti példában olyan hashtáblát hoztam létre, ahol a kulcs mező csak sztring lehet, és az értékek meg csak egészek. Ha egész helyett egy szöveget akartam betölteni, akkor hibát kaptam.

Sorrendtartó szótár

A PowerShell 3.0 verziótól kezdve a szótár adattípusnak van egy beadási sorrendet megtartó változata, amit az [ordered]  típusjelölővel tudunk definiálni:

PS C:\> $sorrendtartó = [ordered] @{}

PS C:\> $sorrendtartó.egy = 1

PS C:\> $sorrendtartó.kettő = 2

PS C:\> $sorrendtartó.három = 3

PS C:\> $sorrendtartó

 

Name                           Value

----                           -----

egy                            1

kettő                          2

három                          3

Látható, hogy itt a sima hashtáblához képest az elemek nem véletlenszerűen, hanem megadásuk sorrendjében lesznek kiolvasva. Ennek a típusnak a hivatalos neve:

PS C:\> $sorrendtartó.GetType().fullname

System.Collections.Specialized.OrderedDictionary

Egyéb változások a hashtáblák kezelésében

A PowerShell 3.0-tól kezdődően néhány apróbb változás is megjelent a hashtáblák kezelésében. Például nem olyan egyszerű azonosnak látszó, csak típusában különböző címkékkel adatokat bevinni:

PS C:\> $hash3 = @{1 = "int"; "1" = "string"}

At line:1 char:23

+ $hash3 = @{1 = "int"; "1" = "string"}

+                       ~~~

Duplicate keys '1' are not allowed in hash literals.

    + CategoryInfo          : ParserError: (:) [], ParentContainsErrorRecordE

   xception

    + FullyQualifiedErrorId : DuplicateKeyInHashLiteral

Ez a 2.0-ás PowerShellben még simán ment, annak ellenére, hogy itt is természetesen helyesen kezeli a típusokat:

PS C:\> $hash3 = @{1 = "int"}

PS C:\> $hash3.GetEnumerator()[0].name.gettype().fullname

System.Int32

PS C:\> $hash3 = @{"1" = "int"}

PS C:\> $hash3.GetEnumerator()[0].name.gettype().fullname

System.String

Azér PowerShell 3.0-ban is ki tudjuk erőltetni ezt a működést, csak a szám formátumú kulcsot zárójelbe kell tenni:

PS C:\> $h = @{(1) = "int"; "1" = "string"}



Word To HTML Converter