Skip to content

Commit

Permalink
Hash: optimize to_a, keys and values (#8042)
Browse files Browse the repository at this point in the history
  • Loading branch information
asterite committed Aug 5, 2019
1 parent 6ab2b81 commit ea77acd
Show file tree
Hide file tree
Showing 2 changed files with 52 additions and 8 deletions.
12 changes: 12 additions & 0 deletions spec/std/hash_spec.cr
Expand Up @@ -817,6 +817,18 @@ describe "Hash" do
h.to_a.should eq([{1, "hello"}, {2, "bye"}])
end

it "does to_a after shift" do
h = {1 => 'a', 2 => 'b', 3 => 'c'}
h.shift
h.to_a.should eq([{2, 'b'}, {3, 'c'}])
end

it "does to_a after delete" do
h = {1 => 'a', 2 => 'b', 3 => 'c'}
h.delete(2)
h.to_a.should eq([{1, 'a'}, {3, 'c'}])
end

it "clears" do
h = {1 => 2, 3 => 4}
h.clear
Expand Down
48 changes: 40 additions & 8 deletions src/hash.cr
Expand Up @@ -1259,10 +1259,8 @@ class Hash(K, V)
# h = {"foo" => "bar", "baz" => "bar"}
# h.keys # => ["foo", "baz"]
# ```
def keys
keys = Array(K).new(@size)
each_key { |key| keys << key }
keys
def keys : Array(K)
to_a_impl &.key
end

# Returns only the values as an `Array`.
Expand All @@ -1271,10 +1269,8 @@ class Hash(K, V)
# h = {"foo" => "bar", "baz" => "qux"}
# h.values # => ["bar", "qux"]
# ```
def values
values = Array(V).new(@size)
each_value { |value| values << value }
values
def values : Array(V)
to_a_impl &.value
end

# Returns the index of the given key, or `nil` when not found.
Expand Down Expand Up @@ -1742,6 +1738,42 @@ class Hash(K, V)
pp.text "{...}" unless executed
end

# Returns an array of tuples with key and values belonging to this Hash.
#
# ```
# h = {1 => 'a', 2 => 'b', 3 => 'c'}
# h.to_a # => [{1, 'a'}, {2, 'b'}, {3, 'c'}]
# ```
def to_a : Array({K, V})
to_a_impl do |entry|
{entry.key, entry.value}
end
end

private def to_a_impl(&block : Entry(K, V) -> U) forall U
index = @first
if @first == @deleted_count
# If the deleted count equals the first element offset it
# means that the only deleted elements are in (0...@first)
# and so all the next ones are non-deleted.
Array(U).new(size) do |i|
value = yield get_entry(index)
index += 1
value
end
else
Array(U).new(size) do |i|
entry = get_entry(index)
while entry.deleted?
index += 1
entry = get_entry(index)
end
index += 1
yield entry
end
end
end

# Returns `self`.
def to_h
self
Expand Down

0 comments on commit ea77acd

Please sign in to comment.