Skip to content

Commit

Permalink
Add Enumerable(T)#to_a(& : T -> U) forall U (#12653)
Browse files Browse the repository at this point in the history
Co-authored-by: Johannes Müller <[email protected]>
  • Loading branch information
caspiano and straight-shoota authored Dec 24, 2023
1 parent af31a4e commit e00a0a4
Show file tree
Hide file tree
Showing 8 changed files with 91 additions and 14 deletions.
10 changes: 10 additions & 0 deletions spec/std/enumerable_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,16 @@ describe "Enumerable" do
end
end

describe "to_a" do
context "with a block" do
SpecEnumerable.new.to_a { |e| e*2 }.should eq [2, 4, 6]
end

context "without a block" do
SpecEnumerable.new.to_a.should eq [1, 2, 3]
end
end

describe "#to_set" do
context "without block" do
it "creates a Set from the unique elements of the collection" do
Expand Down
15 changes: 12 additions & 3 deletions src/enumerable.cr
Original file line number Diff line number Diff line change
Expand Up @@ -1997,9 +1997,18 @@ module Enumerable(T)
# ```
# (1..5).to_a # => [1, 2, 3, 4, 5]
# ```
def to_a
ary = [] of T
each { |e| ary << e }
def to_a : Array(T)
to_a(&.itself)
end

# Returns an `Array` with the results of running *block* against each element of the collection.
#
# ```
# (1..5).to_a { |i| i * 2 } # => [2, 4, 6, 8, 10]
# ```
def to_a(& : T -> U) : Array(U) forall U
ary = [] of U
each { |e| ary << yield e }
ary
end

Expand Down
18 changes: 16 additions & 2 deletions src/hash.cr
Original file line number Diff line number Diff line change
Expand Up @@ -2055,16 +2055,30 @@ class Hash(K, V)
pp.text "{...}" unless executed
end

# Returns an array of tuples with key and values belonging to this Hash.
# Returns an `Array` of `Tuple(K, V)` with key and values belonging to this Hash.
#
# ```
# h = {1 => 'a', 2 => 'b', 3 => 'c'}
# h.to_a # => [{1, 'a'}, {2, 'b'}, {3, 'c'}]
# ```
#
# The order of the array follows the order the keys were inserted in the Hash.
def to_a : Array({K, V})
to_a(&.itself)
end

# Returns an `Array` with the results of running *block* against tuples with key and values
# belonging to this Hash.
#
# ```
# h = {"first_name" => "foo", "last_name" => "bar"}
# h.to_a { |_k, v| v.capitalize } # => ["Foo", "Bar"]
# ```
#
# The order of the array follows the order the keys were inserted in the Hash.
def to_a(&block : {K, V} -> U) : Array(U) forall U
to_a_impl do |entry|
{entry.key, entry.value}
yield ({entry.key, entry.value})
end
end

Expand Down
10 changes: 5 additions & 5 deletions src/indexable.cr
Original file line number Diff line number Diff line change
Expand Up @@ -693,14 +693,14 @@ module Indexable(T)
end
end

# Returns an `Array` with all the elements in the collection.
# Returns an `Array` with the results of running *block* against each element of the collection.
#
# ```
# {1, 2, 3}.to_a # => [1, 2, 3]
# {1, 2, 3}.to_a { |i| i * 2 } # => [2, 4, 6]
# ```
def to_a : Array(T)
ary = Array(T).new(size)
each { |e| ary << e }
def to_a(& : T -> U) : Array(U) forall U
ary = Array(U).new(size)
each { |e| ary << yield e }
ary
end

Expand Down
2 changes: 1 addition & 1 deletion src/llvm/parameter_collection.cr
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ struct LLVM::ParameterCollection
LibLLVM.get_count_params(@function).to_i
end

def to_a
def to_a : Array(LLVM::Value)
param_size = size()
Array(LLVM::Value).build(param_size) do |buffer|
LibLLVM.get_params(@function, buffer.as(LibLLVM::ValueRef*))
Expand Down
15 changes: 14 additions & 1 deletion src/named_tuple.cr
Original file line number Diff line number Diff line change
Expand Up @@ -588,12 +588,25 @@ struct NamedTuple
#
# NOTE: `to_a` on an empty named tuple produces an `Array(Tuple(Symbol, NoReturn))`
def to_a
to_a(&.itself)
end

# Returns an `Array` with the results of running *block* against tuples with key and values belonging
# to this `NamedTuple`.
#
# ```
# tuple = {first_name: "foo", last_name: "bar"}
# tuple.to_a(&.last.capitalize) # => ["Foo", "Bar"]
# ```
#
# NOTE: `to_a` on an empty named tuple produces an `Array(Tuple(Symbol, NoReturn))`
def to_a(&)
{% if T.size == 0 %}
[] of {Symbol, NoReturn}
{% else %}
[
{% for key in T %}
{ {{key.symbolize}}, self[{{key.symbolize}}] },
yield({ {{key.symbolize}}, self[{{key.symbolize}}] }),
{% end %}
]
{% end %}
Expand Down
13 changes: 13 additions & 0 deletions src/set.cr
Original file line number Diff line number Diff line change
Expand Up @@ -382,6 +382,19 @@ struct Set(T)
@hash.keys
end

# Returns an `Array` with the results of running *block* against each element of the collection.
#
# ```
# Set{1, 2, 3, 4, 5}.to_a { |i| i // 2 } # => [0, 1, 2]
# ```
def to_a(& : T -> U) : Array(U) forall U
array = Array(U).new(size)
@hash.each_key do |key|
array << key
end
array
end

# Alias of `#to_s`.
def inspect(io : IO) : Nil
to_s(io)
Expand Down
22 changes: 20 additions & 2 deletions src/tuple.cr
Original file line number Diff line number Diff line change
Expand Up @@ -539,10 +539,28 @@ struct Tuple
to_s
end

def to_a
# Returns an `Array` with all the elements in the tuple.
#
# ```
# {1, 2, 3, 4, 5}.to_a # => [1, 2, 3, 4, 5]
# ```
def to_a : Array(Union(*T))
{% if compare_versions(Crystal::VERSION, "1.1.0") < 0 %}
to_a(&.itself.as(Union(*T)))
{% else %}
to_a(&.itself)
{% end %}
end

# Returns an `Array` with the results of running *block* against each element of the tuple.
#
# ```
# {1, 2, 3, 4, 5}).to_a { |i| i * 2 } # => [2, 4, 6, 8, 10]
# ```
def to_a(& : Union(*T) -> _)
Array(Union(*T)).build(size) do |buffer|
{% for i in 0...T.size %}
buffer[{{i}}] = self[{{i}}]
buffer[{{i}}] = yield self[{{i}}]
{% end %}
size
end
Expand Down

0 comments on commit e00a0a4

Please sign in to comment.