From 38e04e15bf1d4e67c52630fa3fca1d4a056ea768 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alfonso=20Jim=C3=A9nez?= Date: Tue, 20 Nov 2018 11:51:08 +0100 Subject: [PATCH] Adding Enumerable#filter_map --- enum.c | 41 ++++++++++++++++++++++++++++ spec/ruby/core/enumerable/filter_map_spec.rb | 26 ++++++++++++++++++ test/ruby/test_enum.rb | 10 +++++++ test/ruby/test_enumerator.rb | 2 +- 4 files changed, 78 insertions(+), 1 deletion(-) create mode 100644 spec/ruby/core/enumerable/filter_map_spec.rb diff --git a/enum.c b/enum.c index 5b1e3c27e5..8e6a3845a3 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(); @@ -4104,6 +4144,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 @@ def <=> other 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 @@ def test_size_for_enum_created_from_array 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 -- 2.12.1