зеркало из https://github.com/github/ruby.git
Enumerable#tally with the resulting hash [Feature #17744]
This commit is contained in:
Родитель
6a352e275b
Коммит
9143d21b1b
3
NEWS.md
3
NEWS.md
|
@ -23,6 +23,8 @@ Outstanding ones only.
|
|||
|
||||
* Enumerable#compact is added. [[Feature #17312]]
|
||||
|
||||
* Enumerable#tally now accepts an optional hash to count. [[Feature #17744]]
|
||||
|
||||
* Enumerator::Lazy
|
||||
|
||||
* Enumerator::Lazy#compact is added. [[Feature #17312]]
|
||||
|
@ -99,3 +101,4 @@ Excluding feature bug fixes.
|
|||
[Feature #17411]: https://bugs.ruby-lang.org/issues/17411
|
||||
[Bug #17423]: https://bugs.ruby-lang.org/issues/17423
|
||||
[Feature #17479]: https://bugs.ruby-lang.org/issues/17479
|
||||
[Feature #17744]: https://bugs.ruby-lang.org/issues/17744
|
||||
|
|
28
enum.c
28
enum.c
|
@ -688,13 +688,18 @@ enum_to_a(int argc, VALUE *argv, VALUE obj)
|
|||
}
|
||||
|
||||
static VALUE
|
||||
enum_hashify(VALUE obj, int argc, const VALUE *argv, rb_block_call_func *iter)
|
||||
enum_hashify_into(VALUE obj, int argc, const VALUE *argv, rb_block_call_func *iter, VALUE hash)
|
||||
{
|
||||
VALUE hash = rb_hash_new();
|
||||
rb_block_call(obj, id_each, argc, argv, iter, hash);
|
||||
return hash;
|
||||
}
|
||||
|
||||
static VALUE
|
||||
enum_hashify(VALUE obj, int argc, const VALUE *argv, rb_block_call_func *iter)
|
||||
{
|
||||
return enum_hashify_into(obj, argc, argv, iter, rb_hash_new());
|
||||
}
|
||||
|
||||
static VALUE
|
||||
enum_to_h_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, hash))
|
||||
{
|
||||
|
@ -1020,6 +1025,7 @@ tally_up(st_data_t *group, st_data_t *value, st_data_t arg, int existing)
|
|||
tally += INT2FIX(1) & ~FIXNUM_FLAG;
|
||||
}
|
||||
else {
|
||||
Check_Type(tally, T_BIGNUM);
|
||||
tally = rb_big_plus(tally, INT2FIX(1));
|
||||
RB_OBJ_WRITTEN(hash, Qundef, tally);
|
||||
}
|
||||
|
@ -1045,19 +1051,29 @@ tally_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, hash))
|
|||
|
||||
/*
|
||||
* call-seq:
|
||||
* enum.tally -> a_hash
|
||||
* enum.tally -> a_hash
|
||||
* enum.tally(a_hash) -> a_hash
|
||||
*
|
||||
* Tallies the collection, i.e., counts the occurrences of each element.
|
||||
* Returns a hash with the elements of the collection as keys and the
|
||||
* corresponding counts as values.
|
||||
*
|
||||
* ["a", "b", "c", "b"].tally #=> {"a"=>1, "b"=>2, "c"=>1}
|
||||
*
|
||||
* If a hash is given, the number of occurrences is added to each value
|
||||
* in the hash, and the hash is returned. The value corresponding to
|
||||
* each element must be an integer.
|
||||
*/
|
||||
|
||||
static VALUE
|
||||
enum_tally(VALUE obj)
|
||||
enum_tally(int argc, VALUE *argv, VALUE obj)
|
||||
{
|
||||
return enum_hashify(obj, 0, 0, tally_i);
|
||||
VALUE hash;
|
||||
if (rb_check_arity(argc, 0, 1))
|
||||
hash = rb_check_hash_type(argv[0]);
|
||||
else
|
||||
hash = rb_hash_new();
|
||||
return enum_hashify_into(obj, 0, 0, tally_i, hash);
|
||||
}
|
||||
|
||||
NORETURN(static VALUE first_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, params)));
|
||||
|
@ -4393,7 +4409,7 @@ Init_Enumerable(void)
|
|||
rb_define_method(rb_mEnumerable, "reduce", enum_inject, -1);
|
||||
rb_define_method(rb_mEnumerable, "partition", enum_partition, 0);
|
||||
rb_define_method(rb_mEnumerable, "group_by", enum_group_by, 0);
|
||||
rb_define_method(rb_mEnumerable, "tally", enum_tally, 0);
|
||||
rb_define_method(rb_mEnumerable, "tally", enum_tally, -1);
|
||||
rb_define_method(rb_mEnumerable, "first", enum_first, -1);
|
||||
rb_define_method(rb_mEnumerable, "all?", enum_all, -1);
|
||||
rb_define_method(rb_mEnumerable, "any?", enum_any, -1);
|
||||
|
|
|
@ -33,3 +33,31 @@ ruby_version_is "2.7" do
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
ruby_version_is "3.1" do
|
||||
describe "Enumerable#tally with a hash" do
|
||||
before :each do
|
||||
ScratchPad.record []
|
||||
end
|
||||
|
||||
it "returns a hash with counts according to the value" do
|
||||
enum = EnumerableSpecs::Numerous.new('foo', 'bar', 'foo', 'baz')
|
||||
enum.tally({ 'foo' => 1 }).should == { 'foo' => 3, 'bar' => 1, 'baz' => 1}
|
||||
end
|
||||
|
||||
it "ignores the default value" do
|
||||
enum = EnumerableSpecs::Numerous.new('foo', 'bar', 'foo', 'baz')
|
||||
enum.tally(Hash.new(100)).should == { 'foo' => 2, 'bar' => 1, 'baz' => 1}
|
||||
end
|
||||
|
||||
it "ignores the default proc" do
|
||||
enum = EnumerableSpecs::Numerous.new('foo', 'bar', 'foo', 'baz')
|
||||
enum.tally(Hash.new {100}).should == { 'foo' => 2, 'bar' => 1, 'baz' => 1}
|
||||
end
|
||||
|
||||
it "needs the values counting each elements to be an integer" do
|
||||
enum = EnumerableSpecs::Numerous.new('foo')
|
||||
-> { enum.tally({ 'foo' => 'bar' }) }.should raise_error(TypeError)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -394,6 +394,17 @@ class TestEnumerable < Test::Unit::TestCase
|
|||
def test_tally
|
||||
h = {1 => 2, 2 => 2, 3 => 1}
|
||||
assert_equal(h, @obj.tally)
|
||||
|
||||
h = {1 => 5, 2 => 2, 3 => 1, 4 => "x"}
|
||||
assert_equal(h, @obj.tally({1 => 3, 4 => "x"}))
|
||||
|
||||
assert_raise(TypeError) do
|
||||
@obj.tally({1 => ""})
|
||||
end
|
||||
|
||||
h = {1 => 2, 2 => 2, 3 => 1}
|
||||
assert_equal(h, @obj.tally(Hash.new(100)))
|
||||
assert_equal(h, @obj.tally(Hash.new {100}))
|
||||
end
|
||||
|
||||
def test_first
|
||||
|
|
Загрузка…
Ссылка в новой задаче