summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlfonso Jiménez <me@alfie.cat>2018-11-20 11:51:08 +0100
committerNobuyoshi Nakada <nobu@ruby-lang.org>2019-05-23 14:39:16 +0900
commit0acbdd1ed0d2302743525a5188cc5a0d6251680c (patch)
treec6e6787a1f706b36ea7b66094c0868a57864a09e
parent1ccc2eeba08c370d84474357771f0bd7c5fe7f16 (diff)
Adding Enumerable#filter_map
[Feature #15323] Closes: https://github.com/ruby/ruby/pull/2017
-rw-r--r--enum.c41
-rw-r--r--spec/ruby/core/enumerable/filter_map_spec.rb26
-rw-r--r--test/ruby/test_enum.rb10
-rw-r--r--test/ruby/test_enumerator.rb2
4 files changed, 78 insertions, 1 deletions
diff --git a/enum.c b/enum.c
index d754005ab2..d7def94ebf 100644
--- a/enum.c
+++ b/enum.c
@@ -453,6 +453,46 @@ enum_find_all(VALUE obj)
}
static VALUE
+filter_map_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, ary))
+{
+ i = rb_yield_values2(argc, argv);
+
+ if (RTEST(i)) {
+ rb_ary_push(ary, i);
+ }
+
+ return Qnil;
+}
+
+/*
+ * call-seq:
+ * enum.filter_map { |obj| block } -> array
+ * enum.filter_map -> an_enumerator
+ *
+ * Returns a new array containing the truthy results (everything except
+ * +false+ or +nil+) of running the +block+ for every element in +enum+.
+ *
+ * If no block is given, an Enumerator is returned instead.
+ *
+ *
+ * (1..10).filter_map { |i| i * 2 if i.even? } #=> [4, 8, 12, 16, 20]
+ *
+ */
+static VALUE
+enum_filter_map(VALUE obj)
+{
+ VALUE ary;
+
+ RETURN_SIZED_ENUMERATOR(obj, 0, 0, enum_size);
+
+ ary = rb_ary_new();
+ rb_block_call(obj, id_each, 0, 0, filter_map_i, ary);
+
+ return ary;
+}
+
+
+static VALUE
reject_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, ary))
{
ENUM_WANT_SVALUE();
@@ -4108,6 +4148,7 @@ Init_Enumerable(void)
rb_define_method(rb_mEnumerable, "find_all", enum_find_all, 0);
rb_define_method(rb_mEnumerable, "select", enum_find_all, 0);
rb_define_method(rb_mEnumerable, "filter", enum_find_all, 0);
+ rb_define_method(rb_mEnumerable, "filter_map", enum_filter_map, 0);
rb_define_method(rb_mEnumerable, "reject", enum_reject, 0);
rb_define_method(rb_mEnumerable, "collect", enum_collect, 0);
rb_define_method(rb_mEnumerable, "map", enum_collect, 0);
diff --git a/spec/ruby/core/enumerable/filter_map_spec.rb b/spec/ruby/core/enumerable/filter_map_spec.rb
new file mode 100644
index 0000000000..31acc277b4
--- /dev/null
+++ b/spec/ruby/core/enumerable/filter_map_spec.rb
@@ -0,0 +1,26 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+
+ruby_version_is '2.7' do
+ describe 'Enumerable#filter_map' do
+ before :each do
+ @numerous = EnumerableSpecs::Numerous.new(*(1..8).to_a)
+ end
+
+ it 'returns an empty array if there are no elements' do
+ EnumerableSpecs::Empty.new.filter_map { true }.should == []
+ end
+
+ it 'returns an array with truthy results of passing each element to block' do
+ @numerous.filter_map { |i| i * 2 if i.even? }.should == [4, 8, 12, 16]
+ @numerous.filter_map { |i| i * 2 }.should == [2, 4, 6, 8, 10, 12, 14, 16]
+ @numerous.filter_map { 0 }.should == [0, 0, 0, 0, 0, 0, 0, 0]
+ @numerous.filter_map { false }.should == []
+ @numerous.filter_map { nil }.should == []
+ end
+
+ it 'returns an enumerator when no block given' do
+ @numerous.filter_map.should be_an_instance_of(Enumerator)
+ end
+ end
+end
diff --git a/test/ruby/test_enum.rb b/test/ruby/test_enum.rb
index 1029143db6..5fbb2d3663 100644
--- a/test/ruby/test_enum.rb
+++ b/test/ruby/test_enum.rb
@@ -1135,4 +1135,14 @@ class TestEnumerable < Test::Unit::TestCase
end
assert_equal [1, 2, 3, 4, 5], (1..5).sort_by{|e| klass.new e}
end
+
+ def test_filter_map
+ @obj = (1..8).to_a
+ assert_equal([4, 8, 12, 16], @obj.filter_map { |i| i * 2 if i.even? })
+ assert_equal([2, 4, 6, 8, 10, 12, 14, 16], @obj.filter_map { |i| i * 2 })
+ assert_equal([0, 0, 0, 0, 0, 0, 0, 0], @obj.filter_map { 0 })
+ assert_equal([], @obj.filter_map { false })
+ assert_equal([], @obj.filter_map { nil })
+ assert_instance_of(Enumerator, @obj.filter_map)
+ end
end
diff --git a/test/ruby/test_enumerator.rb b/test/ruby/test_enumerator.rb
index 5d1e8f05b1..6bc776dee5 100644
--- a/test/ruby/test_enumerator.rb
+++ b/test/ruby/test_enumerator.rb
@@ -542,7 +542,7 @@ class TestEnumerator < Test::Unit::TestCase
def test_size_for_enum_created_from_enumerable
%i[find_all reject map flat_map partition group_by sort_by min_by max_by
- minmax_by each_with_index reverse_each each_entry].each do |method|
+ minmax_by each_with_index reverse_each each_entry filter_map].each do |method|
assert_equal nil, @obj.send(method).size
assert_equal 42, @sized.send(method).size
end