From 4c2d6a14791efd78fcf1c4cfedfedc4fc12e8be5 Mon Sep 17 00:00:00 2001 From: Joel Drapper Date: Thu, 19 Dec 2024 11:53:33 +0000 Subject: [PATCH 1/3] Literal::Array#zip --- lib/literal/array.rb | 46 ++++++++++++++++++++++++++++ test/array.test.rb | 72 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 118 insertions(+) diff --git a/lib/literal/array.rb b/lib/literal/array.rb index 75b1f23..0a44932 100644 --- a/lib/literal/array.rb +++ b/lib/literal/array.rb @@ -645,4 +645,50 @@ def |(other) def fetch(...) @__value__.fetch(...) end + + def zip(*others) + other_types = others.map do |other| + case other + when Literal::Array + other.__type__ + when Array + _Any? + else + raise ArgumentError + end + end + + tuple = Literal::Tuple( + @__type__, + *other_types + ) + + my_length = length + max_length = [my_length, *others.map(&:length)].max + + # Check we match the max length or our type is nilable + unless my_length == max_length || @__type__ === nil + raise ArgumentError + end + + # Check others match the max length or their types is nilable + others.each_with_index do |other, index| + unless other.length == max_length || other_types[index] === nil + raise ArgumentError + end + end + + i = 0 + result_value = [] + + while i < max_length + result_value << tuple.new( + @__value__[i], + *others.map { |it| it[i] } + ) + i += 1 + end + + __with__(result_value) + end end diff --git a/test/array.test.rb b/test/array.test.rb index 3d266a7..6e106ff 100644 --- a/test/array.test.rb +++ b/test/array.test.rb @@ -842,3 +842,75 @@ [2, 4], ] end + +test "#zip with other literal arrays when all the lengths match" do + a = Literal::Array(String).new("a", "b") + b = Literal::Array(Integer).new(1, 2) + c = Literal::Array(Symbol).new(:a, :b) + + assert_equal a.zip(b, c), Literal::Array( + Literal::Tuple(String, Integer, Symbol) + ).new( + Literal::Tuple(String, Integer, Symbol).new("a", 1, :a), + Literal::Tuple(String, Integer, Symbol).new("b", 2, :b), + ) +end + +test "#zip with other regular arrays" do + a = Literal::Array(String).new("a", "b") + b = [1, 2] + c = [:a, :b] + + assert_equal a.zip(b, c), Literal::Array( + Literal::Tuple(String, _Any?, _Any?) + ).new( + Literal::Tuple(String, _Any?, _Any?).new("a", 1, :a), + Literal::Tuple(String, _Any?, _Any?).new("b", 2, :b), + ) +end + +test "#zip with other literal arrays where one of the others length is not the max length and the type is not nilable" do + a = Literal::Array(String).new("a", "b") + b = Literal::Array(Integer).new(1) + c = Literal::Array(Symbol).new(:a, :b) + + assert_raises ArgumentError do + a.zip(b, c) + end +end + +test "#zip with literal arrays where our length is not the max length and the type is not nilable" do + a = Literal::Array(String).new("a") + b = Literal::Array(Integer).new(1, 2) + c = Literal::Array(Symbol).new(:a, :b) + + assert_raises ArgumentError do + a.zip(b, c) + end +end + +test "#zip when our length is not the max length but the type is nilable" do + a = Literal::Array(_Nilable(String)).new("a") + b = [1, 2] + c = [:a, :b] + + assert_equal a.zip(b, c), Literal::Array( + Literal::Tuple(_Nilable(String), _Any, _Any) + ).new( + Literal::Tuple(_Nilable(String), _Any, _Any).new("a", 1, :a), + Literal::Tuple(_Nilable(String), _Any, _Any).new(nil, 2, :b), + ) +end + +test "#zip with others length is not the max length but the types are nilable" do + a = Literal::Array(String).new("a", "b") + b = Literal::Array(_Nilable(Integer)).new(1) + c = [:a] + + assert_equal a.zip(b, c), Literal::Array( + Literal::Tuple(String, _Nilable(Integer), _Any) + ).new( + Literal::Tuple(String, _Nilable(Integer), _Any).new("a", 1, :a), + Literal::Tuple(String, _Nilable(Integer), _Any).new("b", nil, nil), + ) +end From b3ad5e47e2baa42d1f63c7bf35197c032ee9de72 Mon Sep 17 00:00:00 2001 From: Joel Drapper Date: Thu, 19 Dec 2024 12:01:37 +0000 Subject: [PATCH 2/3] Update array.rb --- lib/literal/array.rb | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/lib/literal/array.rb b/lib/literal/array.rb index 0a44932..d61179f 100644 --- a/lib/literal/array.rb +++ b/lib/literal/array.rb @@ -668,13 +668,25 @@ def zip(*others) # Check we match the max length or our type is nilable unless my_length == max_length || @__type__ === nil - raise ArgumentError + raise ArgumentError.new(<<~MESSAGE) + The literal array could not be zipped becuase its type is not nilable and it has fewer items than the maximum number of items in the other arrays. + + You can either make the type of this array nilable, or add more items so its length matches the others. + + #{inspect} + MESSAGE end # Check others match the max length or their types is nilable others.each_with_index do |other, index| unless other.length == max_length || other_types[index] === nil - raise ArgumentError + raise ArgumentError.new(<<~MESSAGE) + The literal array could not be zipped becuase its type is not nilable and it has fewer items than the maximum number of items in the other arrays. + + You can either make the type of this array nilable, or add more items so its length matches the others. + + #{inspect} + MESSAGE end end From 9e08f3bd2fcdca6566a8d257ee329e71a76128ae Mon Sep 17 00:00:00 2001 From: Joel Drapper Date: Thu, 19 Dec 2024 12:07:50 +0000 Subject: [PATCH 3/3] Support passing a block to `zip` --- lib/literal/array.rb | 31 ++++++++++++++++++++++--------- test/array.test.rb | 17 +++++++++++++++++ 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/lib/literal/array.rb b/lib/literal/array.rb index d61179f..fce9730 100644 --- a/lib/literal/array.rb +++ b/lib/literal/array.rb @@ -691,16 +691,29 @@ def zip(*others) end i = 0 - result_value = [] - while i < max_length - result_value << tuple.new( - @__value__[i], - *others.map { |it| it[i] } - ) - i += 1 - end + if block_given? + while i < max_length + yield tuple.new( + @__value__[i], + *others.map { |it| it[i] } + ) + i += 1 + end - __with__(result_value) + nil + else + result_value = [] + + while i < max_length + result_value << tuple.new( + @__value__[i], + *others.map { |it| it[i] } + ) + i += 1 + end + + __with__(result_value) + end end end diff --git a/test/array.test.rb b/test/array.test.rb index 6e106ff..d23c2ea 100644 --- a/test/array.test.rb +++ b/test/array.test.rb @@ -914,3 +914,20 @@ Literal::Tuple(String, _Nilable(Integer), _Any).new("b", nil, nil), ) end + +test "#zip with a block" do + a = Literal::Array(String).new("a", "b") + b = Literal::Array(Integer).new(1, 2) + c = [:a, :b] + + results = [] + + return_value = a.zip(b, c) { |it| results << it } + + assert_equal results, [ + Literal::Tuple(String, Integer, _Any).new("a", 1, :a), + Literal::Tuple(String, Integer, _Any).new("b", 2, :b), + ] + + assert_equal return_value, nil +end