NAME

Sidef::Types::Hash::Hash - Associative hash table mapping keys to values with convenient collection methods.

DESCRIPTION

This class implements a hash (associative array) data type, providing key-value storage and various methods for hash manipulation, iteration, transformation, and set operations.

A Hash is an unordered collection of key-value pairs where each unique key maps to exactly one value. Keys are compared using structural equality.

SYNOPSIS

# Creating hashes
var hash = Hash(a => 1, b => 2, c => 3)
var h = Hash(:foo, :bar)  # Keys with nil values

# Accessing values
say hash{:a}        #=> 1
say hash.item(:b)   #=> 2

# Setting values
hash{:d} = 4
hash.append(e => 5, f => 6)

# Basic operations
say hash.keys       #=> [:a, :b, :c, :d, :e, :f]
say hash.values     #=> [1, 2, 3, 4, 5, 6]
say hash.len        #=> 6
say hash.has(:a)    #=> true

# Iteration
hash.each { |key, value|
    say "#{key} => #{value}"
}

# Transformation
var doubled  = hash.map_v { _1 * 2 }
var filtered = hash.grep { |k, v| v > 2 }

# Set operations
var h1 = Hash(a => 1, b => 2)
var h2 = Hash(b => 3, c => 4)

say (h1 | h2)   #=> Hash(a => 1, b => 2, c => 4)  # Union
say (h1 & h2)   #=> Hash(b => 2)                  # Intersection
say (h1 - h2)   #=> Hash(a => 1)                  # Difference
say (h1 ^ h2)   #=> Hash(a => 1, c => 4)          # Symmetric difference

INHERITS

Inherits methods from Sidef::Object::Object.

CONSTRUCTION

new

Hash.new
Hash.new(pairs...)

Creates and returns a new Hash object, optionally initialized with the given key-value pairs.

var h1 = Hash.new
var h2 = Hash.new(a => 1, b => 2)
var h3 = Hash(a => 1, b => 2)  # Shorthand

Aliases: call

ACCESSING VALUES

item

hash.item(key)

Returns the value associated with the given key, or nil if the key does not exist.

var h = Hash(a => 1, b => 2)

say h.item(:a)  #=> 1
say h.item(:z)  #=> nil

This is equivalent to subscript notation: hash{key}.

items

hash.items(keys...)

Returns an Array of values for the specified keys. Returns nil for keys that do not exist.

var h = Hash(a => 1, b => 2, c => 3)
say h.items(:a, :c, :z)  #=> [1, 3, nil]

fetch

hash.fetch(key, default)

Returns the value for the given key if it exists; otherwise returns the default value.

var h = Hash(a => 1, b => 2)

say h.fetch(:a, 0)    #=> 1
say h.fetch(:z, 0)    #=> 0
say h.fetch(:z, nil)  #=> nil

Unlike direct subscript access (hash{key}), fetch allows you to distinguish between a missing key and a key whose value is nil.

dig

hash.dig(key, keys...)

Recursively retrieves a nested value by traversing a chain of keys. Returns the value at the nested key path, or nil if any key in the path does not exist.

var h = Hash(
    a => Hash(
        b => Hash(
            c => 42
        )
    )
)

say h.dig(:a, :b, :c)  #=> 42
say h.dig(:a, :x)      #=> nil

This is safer than chaining subscript operations when intermediate keys may be absent.

TESTING AND COUNTING

has

hash.has(key)

Returns true if the given key exists in the hash, false otherwise.

var h = Hash(a => 1, b => nil)

say h.has(:a)  #=> true
say h.has(:b)  #=> true  (key exists even with nil value)
say h.has(:z)  #=> false

Aliases: exists, has_key, haskey, contain, contains, include, includes

is_empty

hash.is_empty

Returns true if the hash contains no key-value pairs, false otherwise.

var h1 = Hash()
var h2 = Hash(a => 1)

say h1.is_empty  #=> true
say h2.is_empty  #=> false

same_keys

hash.same_keys(other)

Returns true if the hash has exactly the same set of keys as other (another hash), false otherwise. Values are not compared.

var h1 = Hash(a => 1, b => 2)
var h2 = Hash(a => 10, b => 20)
var h3 = Hash(a => 1, c => 3)

say h1.same_keys(h2)  #=> true
say h1.same_keys(h3)  #=> false

len

hash.len

Returns the number of key-value pairs in the hash.

var h = Hash(a => 1, b => 2, c => 3)
say h.len  #=> 3

Aliases: size, length

count

hash.count(key)
hash.count { |key, value| ... }

When called with a key, returns 1 if the key exists in the hash, 0 otherwise.

When called with a block, returns the number of key-value pairs for which the block returns true.

var h = Hash(a => 1, b => 2, c => 3, d => 4)

say h.count(:b)                #=> 1
say h.count(:z)                #=> 0
say h.count { |k, v| v > 2 }  #=> 2

count_by

hash.count_by { |key, value| ... }

Returns the number of key-value pairs for which the block returns a true value. The block receives (key, value) as arguments.

var h = Hash(a => 1, b => 2, c => 3, d => 4)
say h.count_by { |k, v| v.is_even }  #=> 2

KEYS, VALUES, AND PAIRS

keys

hash.keys

Returns an Array containing all keys in the hash.

var h = Hash(a => 1, b => 2, c => 3)
say h.keys  #=> [:a, :b, :c]

Note: Hash keys are unordered, so the order of keys in the returned array is not guaranteed.

values

hash.values

Returns an Array containing all values in the hash.

var h = Hash(a => 1, b => 2, c => 3)
say h.values  #=> [1, 2, 3]

Note: Since hash keys are unordered, the order of values in the returned array is not guaranteed.

kv

hash.kv

Returns an Array of Pair objects, where each Pair contains a key and its associated value.

var h = Hash(a => 1, b => 2)
say h.kv  #=> [Pair(:a, 1), Pair(:b, 2)]

# Useful for iteration with destructuring
h.kv.each { |pair|
    say "#{pair.key} => #{pair.value}"
}

Aliases: to_a, pairs, to_array

get_pair

hash.get_pair(key)

Returns a Pair object containing the given key and its associated value.

var h = Hash(a => 1, b => 2)
var p = h.get_pair(:a)
say p        #=> Pair(:a, 1)
say p.key    #=> :a
say p.value  #=> 1

Returns a Pair with a nil value if the key does not exist.

get_pairs

hash.get_pairs(keys...)

Returns an Array of Pair objects for the specified keys.

var h = Hash(a => 1, b => 2, c => 3)
var pairs = h.get_pairs(:a, :c)
say pairs  #=> [Pair(:a, 1), Pair(:c, 3)]

Missing keys produce Pair objects with nil values.

slice

hash.slice(keys...)

Returns a new hash containing only the specified keys. Keys that exist in the original hash keep their values, and keys that do not exist are included in the result with nil values.

var h = Hash(a => 1, b => 2, c => 3, d => 4)
var subset = h.slice(:a, :c, :z)
say subset  #=> Hash(a => 1, c => 3, z => nil)

Note: Missing keys are included with nil values. See lsel for the Array/to_list variant of this operation.

lsel

hash.lsel(keys_array)

Performs a linear selection: returns a new hash containing the keys specified in keys_array, including missing keys with nil values. The main difference from slice is the argument shape: lsel takes an Array (or any object responding to to_list), while slice takes variadic keys.

var h = Hash(a => 1, b => 2)
var selected = h.lsel([:a, :c])
say selected  #=> Hash(a => 1, c => nil)

The argument must be an Array (or any object responding to to_list).

Aliases: linsel, linear_selection

MODIFYING THE HASH

append

hash.append(pairs...)

Appends the given key-value pairs to the hash in-place and returns the modified hash. If a key already exists, its value is updated.

var h = Hash(a => 1)
h.append(b => 2, c => 3)
say h  #=> Hash(a => 1, b => 2, c => 3)

clear

hash.clear

Removes all key-value pairs from the hash in-place and returns the now-empty hash.

var h = Hash(a => 1, b => 2)
h.clear
say h      #=> Hash()
say h.len  #=> 0

delete

hash.delete(keys...)

Removes the specified keys from the hash in-place and returns the deleted values as an Array (or a single value if only one key was given).

var h = Hash(a => 1, b => 2, c => 3)
say h.delete(:b)      #=> 2
say h.delete(:a, :c)  #=> [1, 3]
say h                 #=> Hash()

Aliases: remove

delete_if

hash.delete_if { |key, value| ... }

Deletes all key-value pairs for which the block returns true. The block receives (key, value) as arguments. Returns the modified hash.

var h = Hash(a => 1, b => 2, c => 3, d => 4)
h.delete_if { |k, v| v.is_even }
say h  #=> Hash(a => 1, c => 3)

This is the in-place counterpart to grep, which returns a new filtered hash instead of modifying the original.

set_keys

hash.set_keys(keys...)

Sets the specified keys to nil in the hash in-place and returns the modified hash.

var h = Hash(a => 1)
h.set_keys(:b, :c)
say h  #=> Hash(a => 1, b => nil, c => nil)

This is useful for pre-initializing keys before assigning their actual values.

merge_values

hash.merge_values(other_hash)

Updates the values in the hash for keys that also exist in other_hash. Only keys already present in the original hash are affected; no new keys are added. Modifies the hash in-place and returns it.

var h1 = Hash(a => 1, b => 2, c => 3)
var h2 = Hash(b => 20, c => 30, d => 40)

h1.merge_values(h2)
say h1  #=> Hash(a => 1, b => 20, c => 30)

Note: Key :d from h2 was not added because it did not already exist in h1.

ITERATION

each

hash.each { |key, value| ... }
hash.each

Iterates over each key-value pair in the hash, calling the block with (key, value). When called without a block, acts as a stateful iterator, returning one key-value pair per call (or nil when the iteration is exhausted).

var h = Hash(a => 1, b => 2, c => 3)

h.each { |k, v|
    say "#{k} => #{v}"
}

# Stateful iterator usage
loop {
    var (k, v) = h.each \\ break
    say "#{k} => #{v}"
}

Aliases: each_kv, each_pair

each_k

hash.each_k { |key| ... }

Iterates over each key in the hash, calling the block with the key. Returns the hash.

var h = Hash(a => 1, b => 2, c => 3)
h.each_k { |k|
    say k
}

Aliases: each_key

each_v

hash.each_v { |value| ... }

Iterates over each value in the hash, calling the block with the value. Returns the hash.

var h = Hash(a => 1, b => 2, c => 3)
h.each_v { |v|
    say v * 2
}

Aliases: each_value

TRANSFORMATION AND FILTERING

map

hash.map { |key, value| ... }

Transforms the hash by applying the block to each key-value pair. The block receives (key, value) and should return a new key-value pair as a flat two-element list. Returns a new hash built from the collected pairs.

var h = Hash(a => 1, b => 2)
var mapped = h.map { |k, v|
    (k.uc, v * 10)
}
say mapped  #=> Hash(A => 10, B => 20)

Aliases: map_kv

map_v

hash.map_v { |key, value| ... }

Returns a new hash with the same keys but with values transformed by the block. The block receives (key, value) and should return the new value.

var h = Hash(a => 1, b => 2, c => 3)
var doubled = h.map_v { |k, v| v * 2 }
say doubled  #=> Hash(a => 2, b => 4, c => 6)

# The block may also take just the value
var squared = h.map_v { _1 ** 2 }
say squared  #=> Hash(a => 1, b => 4, c => 9)

Aliases: map_val

grep

hash.grep { |key, value| ... }

Returns a new hash containing only the key-value pairs for which the block returns true. The block receives (key, value) as arguments.

var h = Hash(a => 1, b => 2, c => 3, d => 4)
var evens = h.grep { |k, v| v.is_even }
say evens  #=> Hash(b => 2, d => 4)

This is the non-destructive counterpart to delete_if.

Aliases: grep_kv, select

grep_v

hash.grep_v { |value| ... }

Returns a new hash containing only the key-value pairs for which the block (called with the value only) returns true.

var h = Hash(a => 1, b => 2, c => 3, d => 4)
var evens = h.grep_v { .is_even }
say evens  #=> Hash(b => 2, d => 4)

Aliases: grep_val

collect

hash.collect { |key, value| ... }

Iterates over each key-value pair, calling the block with (key, value), and collects all return values into a new Array.

var h = Hash(a => 1, b => 2, c => 3)
var result = h.collect { |k, v|
    "#{k}=#{v}"
}
say result  #=> ["a=1", "b=2", "c=3"]

Aliases: collect_kv

flip

hash.flip

Returns a new hash with keys and values swapped. The original values become the new keys, and the original keys become the new values.

var h = Hash(a => 1, b => 2, c => 3)
say h.flip  #=> Hash(1 => :a, 2 => :b, 3 => :c)

Note: If multiple keys share the same value, only one key-value pair will survive in the flipped hash (the last one encountered wins).

Aliases: invert, reverse

SORTING

sort

hash.sort
hash.sort { |a, b| ... }

Returns an Array of Pairs sorted by key. When no block is given, keys are sorted in lexicographic order. When a block is provided, it is used as a comparator for the keys.

var h = Hash(c => 3, a => 1, b => 2)
say h.sort  #=> [Pair(:a, 1), Pair(:b, 2), Pair(:c, 3)]

# Custom comparator (reverse order)
var rev = h.sort { |a, b| b <=> a }
say rev  #=> [Pair(:c, 3), Pair(:b, 2), Pair(:a, 1)]

sort_by

hash.sort_by { |key, value| ... }

Returns an Array of Pairs sorted by the values returned by the block. The block receives (key, value) as arguments.

var h = Hash(a => 3, b => 1, c => 2)
var sorted = h.sort_by { |k, v| v }
say sorted  #=> [Pair(:b, 1), Pair(:c, 2), Pair(:a, 3)]

# Sort by key length
var by_len = h.sort_by { |k, v| k.len }

min_by

hash.min_by { |key, value| ... }

Returns the key-value Pair for which the block returns the minimum value. The block receives (key, value) as arguments.

var h = Hash(a => 3, b => 1, c => 5, d => 2)
var min_pair = h.min_by { |k, v| v }
say min_pair  #=> Pair(:b, 1)

max_by

hash.max_by { |key, value| ... }

Returns the key-value Pair for which the block returns the maximum value. The block receives (key, value) as arguments.

var h = Hash(a => 3, b => 1, c => 5, d => 2)
var max_pair = h.max_by { |k, v| v }
say max_pair  #=> Pair(:c, 5)

OPERATORS

==

hash1 == hash2

Returns true if both hashes have the same keys with equal values (structural equality).

var h1 = Hash(a => 1, b => 2)
var h2 = Hash(b => 2, a => 1)
say (h1 == h2)  #=> true

Aliases: eq

hash1 ≠ hash2

Returns true if the two hashes are not structurally equal (different keys or different values).

var h1 = Hash(a => 1)
var h2 = Hash(a => 2)
say (h1 ≠ h2)  #=> true

Aliases: !=, ne

+

hash1 + hash2

Returns a new hash containing all key-value pairs from both hashes. If a key exists in both, the value from the second hash takes precedence (right-hand side wins).

var h1 = Hash(a => 1, b => 2)
var h2 = Hash(b => 20, c => 30)
say (h1 + h2)  #=> Hash(a => 1, b => 20, c => 30)

Aliases: merge, concat

-

hash1 - hash2

Returns the set difference of two hashes. The result contains only keys that exist in the first hash but not in the second.

var h1 = Hash(a => 1, b => 2, c => 3)
var h2 = Hash(b => 20, d => 40)
say (h1 - h2)  #=> Hash(a => 1, c => 3)

Aliases: sub, diff, difference

&

hash1 & hash2

Returns the intersection of two hashes. The result contains only keys that exist in both hashes, with values taken from the first hash.

var h1 = Hash(a => 1, b => 2, c => 3)
var h2 = Hash(b => 20, c => 30, d => 40)
say (h1 & h2)  #=> Hash(b => 2, c => 3)

Aliases: and, intersection

|

hash1 | hash2

Returns the union of two hashes. The result contains all keys from both hashes; for keys present in both, the value from the first hash takes precedence (left-hand side wins).

var h1 = Hash(a => 1, b => 2)
var h2 = Hash(b => 20, c => 30)
say (h1 | h2)  #=> Hash(a => 1, b => 2, c => 30)

Note the difference from + (merge): union keeps the left-hand value for common keys, while merge keeps the right-hand value.

Aliases: or, union

^

hash1 ^ hash2

Returns the symmetric difference of two hashes. The result contains keys that exist in either hash but not in both.

var h1 = Hash(a => 1, b => 2)
var h2 = Hash(b => 20, c => 30)
say (h1 ^ h2)  #=> Hash(a => 1, c => 30)

Aliases: xor, symdiff, symmetric_difference

...

hash...

Returns the hash as a flat list of alternating keys and values. This is useful for unpacking a hash into an argument list or for converting it to other data structures.

var h = Hash(a => 1, b => 2)
say h...  #=> [:a, 1, :b, 2]

Aliases: to_list

CONVERSION

dump

hash.dump

Returns a string representation of the hash.

var h = Hash(a => 1, b => 2)
say h.dump  #=> "Hash(a => 1, b => 2)"

Aliases: to_s, to_str

to_set

hash.to_set

Converts the hash's keys to a Set object. The hash values are ignored.

var h = Hash(a => 1, b => 2, c => 3)
var set = h.to_set
say set  #=> Set(:a, :b, :c)

to_bag

hash.to_bag

Converts the hash's keys to a Bag object. The hash values are ignored.

var h = Hash(a => 1, b => 2, c => 3)
var bag = h.to_bag
say bag  #=> Bag(:a, :b, :c)

as_tree

hash.as_tree(root)

Returns the hash as a tree structure represented as nested Pairs, using the given root as the tree's root label. Nested hashes are recursively converted to sub-trees. Non-hash leaf values are represented as Pairs with an empty array []; only the key hierarchy is preserved, not the leaf values themselves.

var h = Hash(
    a => Hash(b => 1),
    c => 2
)
say h.as_tree(:root)
#=> Pair(:root, [Pair(:a, [Pair(:b, [])]), Pair(:c, [])])

This is useful for visualizing or processing hashes as hierarchical data structures.

EXAMPLES

Frequency Counting

var text = "hello world"
var freq = Hash()

text.each_char { |char|
    freq{char} := 0
    freq{char}++
}

say freq  #=> Hash(h => 1, e => 1, l => 3, ...)

Grouping Data

var people = [
    Hash(name => "Alice", age => 30),
    Hash(name => "Bob",   age => 25),
    Hash(name => "Carol", age => 30)
]

var by_age = Hash()
people.each { |person|
    var age = person{:age}
    by_age{age} := []
    by_age{age} << person
}

Transforming Data

var prices = Hash(apple => 1.50, banana => 0.75, orange => 2.00)

# Apply a discount
var discounted = prices.map_v { _1 * 0.9 }

# Filter expensive items
var expensive = prices.grep { |k, v| v > 1.00 }

Merging with Custom Logic

var defaults = Hash(color => :red, size => :medium, count => 1)
var options  = Hash(size => :large)

# Keep defaults for missing keys
var config = defaults + options
say config  #=> Hash(color => :red, size => :large, count => 1)

SEE ALSO

Sidef::Types::Array::Array, Sidef::Types::Set::Set, Sidef::Types::Bag::Bag