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

Make Array#transpose, Enumerable#reject, Enumerable#to_h work with tuples #10445

Merged
10 changes: 10 additions & 0 deletions spec/std/array_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -1830,6 +1830,16 @@ describe "Array" do
expect_raises(IndexError) { [[1], [1, 2]].transpose }
expect_raises(IndexError) { [[1, 2], [1]].transpose }
end

it "transposes array of tuples" do
[{1, 1.0}].transpose.should eq([[1], [1.0]])
[{1}, {1.0}].transpose.should eq([[1, 1.0]])
[{1, 1.0}, {'a', "aaa"}].transpose.should eq([[1, 'a'], [1.0, "aaa"]])

typeof([{1, 1.0}].transpose).should eq(Array(Array(Int32 | Float64)))
typeof([{1}, {1.0}].transpose).should eq(Array(Array(Int32 | Float64)))
typeof([{1, 1.0}, {'a', "aaa"}].transpose).should eq(Array(Array(String | Int32 | Float64 | Char)))
end
end

describe "rotate" do
Expand Down
10 changes: 10 additions & 0 deletions spec/std/enumerable_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -896,6 +896,12 @@ describe "Enumerable" do
ints.should eq([1, 3])
ints.should be_a(Array(Int32))
end

it "with type, for tuples" do
ints = {1, true, false, 3}.reject(Int32)
ints.should eq([true, false])
ints.should be_a(Array(Bool))
end
end

describe "sample" do
Expand Down Expand Up @@ -1132,6 +1138,10 @@ describe "Enumerable" do
hash = Tuple.new({:a, 1}, {:c, 2}).to_h
hash.should be_a(Hash(Symbol, Int32))
hash.should eq({:a => 1, :c => 2})

hash = Tuple.new({1, 1.0}, {'a', "aaa"}).to_h
hash.should be_a(Hash(Int32 | Char, Float64 | String))
hash.should eq({1 => 1.0, 'a' => "aaa"})
end

it "for array" do
Expand Down
8 changes: 4 additions & 4 deletions src/array.cr
Original file line number Diff line number Diff line change
Expand Up @@ -1263,7 +1263,7 @@ class Array(T)
end

def self.product(arrays)
result = [] of Array(typeof(arrays.first.first))
result = [] of Array(typeof(arrays.first_internal.first_internal))
each_product(arrays) do |product|
result << product
end
Expand Down Expand Up @@ -1829,16 +1829,16 @@ class Array(T)
# a # => [[:a, :b], [:c, :d], [:e, :f]]
# ```
def transpose
return Array(Array(typeof(first.first))).new if empty?
return Array(Array(typeof(first_internal.first_internal))).new if empty?

len = self[0].size
(1...@size).each do |i|
l = self[i].size
raise IndexError.new if len != l
end

Array(Array(typeof(first.first))).new(len) do |i|
Array(typeof(first.first)).new(@size) do |j|
Array(Array(typeof(first_internal.first_internal))).new(len) do |i|
Array(typeof(first_internal.first_internal)).new(@size) do |j|
self[j][i]
end
end
Expand Down
18 changes: 12 additions & 6 deletions src/enumerable.cr
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,7 @@ module Enumerable(T)
# ["Alice", "Bob"].compact_map { |name| name.match(/^A./) } # => [Regex::MatchData("Al")]
# ```
def compact_map
ary = [] of typeof((yield first).not_nil!)
ary = [] of typeof((yield first_internal).not_nil!)
each do |e|
v = yield e
unless v.is_a?(Nil)
Expand Down Expand Up @@ -525,7 +525,7 @@ module Enumerable(T)
# array # => ['A', 'l', 'i', 'c', 'e', 'B', 'o', 'b']
# ```
def flat_map(&block : T -> _)
ary = [] of typeof(flat_map_type(yield first))
ary = [] of typeof(flat_map_type(yield first_internal))
each do |e|
case v = yield e
when Array, Iterator
Expand Down Expand Up @@ -1233,7 +1233,7 @@ module Enumerable(T)
# ```
def reject(type : U.class) forall U
ary = [] of typeof(begin
e = first
e = first_internal
e.is_a?(U) ? raise("") : e
end)
each { |e| ary << e unless e.is_a?(U) }
Expand Down Expand Up @@ -1480,7 +1480,7 @@ module Enumerable(T)
# ([] of Int32).sum { |x| x + 1 } # => 0
# ```
def sum(&block)
sum(additive_identity(Reflect(typeof(yield first)))) do |value|
sum(additive_identity(Reflect(typeof(yield first_internal)))) do |value|
yield value
end
end
Expand Down Expand Up @@ -1559,7 +1559,7 @@ module Enumerable(T)
# ([] of Int32).product { |x| x + 1 } # => 1
# ```
def product(&block)
product(Reflect(typeof(yield first)).first.multiplicative_identity) do |value|
product(Reflect(typeof(yield first_internal)).first.multiplicative_identity) do |value|
yield value
end
end
Expand Down Expand Up @@ -1650,7 +1650,7 @@ module Enumerable(T)
# Tuple.new({:a, 1}, {:c, 2}).to_h # => {:a => 1, :c => 2}
# ```
def to_h
each_with_object(Hash(typeof(first[0]), typeof(first[1])).new) do |item, hash|
each_with_object(Hash(typeof(first_internal[0]), typeof(first_internal[1])).new) do |item, hash|
hash[item[0]] = item[1]
end
end
Expand Down Expand Up @@ -1911,4 +1911,10 @@ module Enumerable(T)
{% end %}
end
end

# :nodoc:
def first_internal
straight-shoota marked this conversation as resolved.
Show resolved Hide resolved
# overridden in Tuple to disable literal index lookup
first
end
end
6 changes: 6 additions & 0 deletions src/tuple.cr
Original file line number Diff line number Diff line change
Expand Up @@ -587,4 +587,10 @@ struct Tuple
self[{{T.size - 1}}]
{% end %}
end

# :nodoc:
def first_internal
i = 0
self[i]
end
HertzDevil marked this conversation as resolved.
Show resolved Hide resolved
end