Definition:
In general, a Hash Table, or Hash Array, or Associative array, or shortly Hash is an array- like data structure, to store some data with an associated key for each; so, ‘atom’ of a hash is a pair of a ‘key’ with a ‘value’. A hash system needs to perform at least three operations:
– add a new pair,
– access to value via key
– the search and delete operations on a key pair
In Harbour, a hash is simply a special array, or more precisely a “keyed” array with special syntax with a set of functions.
Building:
The “=>” operator can be used to indicate literally the relation between <key> <value> pair: <key> => <value>
We can define and initialize a hash by this “literal” way :
hDigits_1 := { 1 => 1, 2 => 2, 3 => 3, 4 => 4 }
or by a special function call:
hDigits_1 := HB_HASH( 1, 1, 2, 2, 3, 3, 4, 4 )
Using “add” method may be another way :
hDigits_1 := { => } // Build an empty hash
hDigits_1[ 1] := 1
hDigits_1[ 2] := 2
hDigits_1[ 3] := 3
hDigits_1[ 4] := 4
In this method while evaluating each of above assignments, if given key exits in hash, will be replaced its value; else add a new pair to the hash.
In addition, data can be added to a hash by extended “+=” operator:
hCountries := { 'Argentina' => "Buenos Aires" }
hCountries += { 'Brasil' => "Brasilia" }
hCountries += { 'Chile' => "Santiago" }
hCountries += { 'Mexico' => "Mexico City" }
Hashs may add ( concatenate ) each other by extended “+” sign :
hFruits := { "fruits" => { "apple", "chery", "apricot" } }
hDays := { "days" => { "sunday", "monday" } }
hDoris := hFruits + hDays
Note: This “+” and “+=” operators depends xHB lib and needs to xHB lib and xHB.ch.
Typing :
<key> part of a hash may be any legal scalar type : C, D, L, N; and <value> part may be in addition scalar types, any complex type ( array or hash ).
Correction : This definition is wrong ! The correct is :
<key> entry key; can be of type: number, date, datetime, string, pointer.
Corrected at : 2015.12.08; thanks to Marek.
hDigits_2 := { 1 => “One”, 2 => “Two”, 3 => “Three”, 4 => “Four” }
hDigits_3 := { "1" => "One", "2" => "Two", "3" => "Three", "4" => "Four" }
hDigits_4 := { "1" => "One", 2 => "Two", 3 => "Three", "4" => "Four" }
hDigits_5 := { 1 => "One", 1 => "Two", 3 => "Three", 4 => "Four"
All of these examples are legal. As a result, a pair record of a hash may be:
– Numeric key, numeric value ( hDigits_1 )
– Numeric key, character value ( hDigits_2 )
– Character key, character value ( hDigits_3 )
– Mixed type key ( hDigits_4 )
Duplicate keys (as seen in hDigits_5) is permitted to assign, but not give a result such as double keyed values: LEN( hDigits_5 ) is 3, not 4; because first pair replaced by second due to has same key.
Consider a table-like data for customers records with two character fields: Customer ID and customer name:
Cust_ID | Cust_Name |
CC001 | Pierce Firth |
CC002 | Stellan Taylor |
CC003 | Chris Cherry |
CC004 | Amanda Baranski |
We can build a hash with this data :
hCustomers := { "CC001" => "Pierce Firth",;
"CC002" => "Stellan Taylor",;
"CC003" => "Chris Cherry",;
"CC004" => "Amanda Baranski" }
and list it:
?
? "Listing a hash :"
?
h1Record := NIL
FOR EACH h1Record IN hCustomers
? cLMarj, h1Record:__ENUMKEY(), h1Record:__ENUMVALUE()
NEXT
Accessing a specific record is easy :
hCustomers[ "CC003" ] // Chris Cherry *-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._. /* Hash Basics */ #include "xhb.ch" #define NTrim( n ) LTRIM( STR( n ) ) PROCEDURE Main() SET DATE GERM SET CENT ON SET COLO TO "W/B" cLMarj := SPACE( 3 ) CLS hDigits_1 := { => } // Build an empty hash hDigits_1[ 1 ] := 1 hDigits_1[ 2 ] := 2 hDigits_1[ 3 ] := 3 hDigits_1[ 4 ] := 4 ListHash( hDigits_1, "Digits_1" ) hDigits_2 := HB_HASH( 1, 1, 2, 2, 3, 3, 4, 4 ) ListHash( hDigits_2, "Digits_2" ) hDigits_3 := { 1 => 1,; 2 => 2,; 3 => 3,; 4 => 4 } ListHash( hDigits_3, "Digits_3" ) hDigits_4 := { 1 => "One",; 2 => "Two",; 3 => "Three",; 4 => "Four" } ListHash( hDigits_4, "Digits_4" ) hDigits_5 := { "1" => "One",; "2" => "Two",; "3" => "Three",; "4" => "Four" } ListHash( hDigits_5, "Digits_5" ) hDigits_6 := { "1" => "One",; 2 => "Two",; 3 => "Three",; "4" => "Four" } ListHash( hDigits_6, "Digits_6" ) hDigits_7 := { 1 => "One",; 1 => "Two",; // This line replace to previous due to same key 3 => "Three",; 4 => "Four" } ListHash( hDigits_7, "Digits_7" ) * WAIT "EOF digits" hCustomers := { "CC001" => "Pierce Firth",; "CC002" => "Stellan Taylor",; "CC003" => "Chris Cherry",; "CC004" => "Amanda Baranski" } ListHash( hCustomers, "A hash defined and initialized literally" ) ? ? "Hash value with a specific key (CC003) :", hCustomers[ "CC003" ] // Chris Cherry ? cKey := "CC003" ? ? "Locating a specific record in an hash by key (", cKey, ":" ? c1Data := hCustomers[ cKey ] ? cLMarj, c1Data hCountries := { 'Argentina' => "Buenos Aires" } hCountries += { 'Brasil' => "Brasilia" } hCountries += { 'Chile' => "Santiago" } hCountries += { 'Mexico' => "Mexico City" } ListHash( hCountries, "A hash defined and initialized by adding with '+=' operator:" ) hFruits := { "fruits" => { "apple", "chery", "apricot" } } hDays := { "days" => { "sunday", "monday" } } hDoris := hFruits + hDays ListHash( hDoris, "A hash defined and initialized by concataned two hash with '+' operator:" ) ? @ MAXROW(), 0 WAIT "EOF HashBasics.prg" RETURN // HashBasics.Main() *-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._. PROCEDURE ListHash( hHash, cComment ) LOCAL x1Pair := NIL cComment := IF( HB_ISNIL( cComment ), '', cComment ) ? ? cComment, "-- Type :", VALTYPE( hHash ), "size:", NTrim ( LEN( hHash ) ) ? FOR EACH x1Pair IN hHash nIndex := x1Pair:__ENUMINDEX() x1Key := x1Pair:__ENUMKEY() x1Value := x1Pair:__ENUMVALUE() ? cLMarj, NTrim( nIndex ) * ?? '', VALTYPE( x1Pair ) ?? '', x1Key, "=>" * ?? '', VALTYPE( x1Key ) * ?? VALTYPE( x1Value ) IF HB_ISARRAY( x1Value ) AEVAL( x1Value, { | x1 | QQOUT( '', x1 ) } ) ELSE ?? '', x1Value ENDIF NEXT RETURN // ListHash() *-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.