Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Enumerable(T)#to_a(& : T -> U) forall U #12653

Merged
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