mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
Enumerable#tally with the resulting hash [Feature #17744]
This commit is contained in:
parent
6a352e275b
commit
9143d21b1b
Notes:
git
2021-03-26 16:29:47 +09:00
Merged: https://github.com/ruby/ruby/pull/4318 Merged-By: nobu <nobu@ruby-lang.org>
4 changed files with 64 additions and 6 deletions
3
NEWS.md
3
NEWS.md
|
@ -23,6 +23,8 @@ Outstanding ones only.
|
||||||
|
|
||||||
* Enumerable#compact is added. [[Feature #17312]]
|
* Enumerable#compact is added. [[Feature #17312]]
|
||||||
|
|
||||||
|
* Enumerable#tally now accepts an optional hash to count. [[Feature #17744]]
|
||||||
|
|
||||||
* Enumerator::Lazy
|
* Enumerator::Lazy
|
||||||
|
|
||||||
* Enumerator::Lazy#compact is added. [[Feature #17312]]
|
* Enumerator::Lazy#compact is added. [[Feature #17312]]
|
||||||
|
@ -99,3 +101,4 @@ Excluding feature bug fixes.
|
||||||
[Feature #17411]: https://bugs.ruby-lang.org/issues/17411
|
[Feature #17411]: https://bugs.ruby-lang.org/issues/17411
|
||||||
[Bug #17423]: https://bugs.ruby-lang.org/issues/17423
|
[Bug #17423]: https://bugs.ruby-lang.org/issues/17423
|
||||||
[Feature #17479]: https://bugs.ruby-lang.org/issues/17479
|
[Feature #17479]: https://bugs.ruby-lang.org/issues/17479
|
||||||
|
[Feature #17744]: https://bugs.ruby-lang.org/issues/17744
|
||||||
|
|
26
enum.c
26
enum.c
|
@ -688,13 +688,18 @@ enum_to_a(int argc, VALUE *argv, VALUE obj)
|
||||||
}
|
}
|
||||||
|
|
||||||
static VALUE
|
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);
|
rb_block_call(obj, id_each, argc, argv, iter, hash);
|
||||||
return 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
|
static VALUE
|
||||||
enum_to_h_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, hash))
|
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;
|
tally += INT2FIX(1) & ~FIXNUM_FLAG;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
Check_Type(tally, T_BIGNUM);
|
||||||
tally = rb_big_plus(tally, INT2FIX(1));
|
tally = rb_big_plus(tally, INT2FIX(1));
|
||||||
RB_OBJ_WRITTEN(hash, Qundef, tally);
|
RB_OBJ_WRITTEN(hash, Qundef, tally);
|
||||||
}
|
}
|
||||||
|
@ -1046,18 +1052,28 @@ tally_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, hash))
|
||||||
/*
|
/*
|
||||||
* call-seq:
|
* 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.
|
* Tallies the collection, i.e., counts the occurrences of each element.
|
||||||
* Returns a hash with the elements of the collection as keys and the
|
* Returns a hash with the elements of the collection as keys and the
|
||||||
* corresponding counts as values.
|
* corresponding counts as values.
|
||||||
*
|
*
|
||||||
* ["a", "b", "c", "b"].tally #=> {"a"=>1, "b"=>2, "c"=>1}
|
* ["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
|
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)));
|
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, "reduce", enum_inject, -1);
|
||||||
rb_define_method(rb_mEnumerable, "partition", enum_partition, 0);
|
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, "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, "first", enum_first, -1);
|
||||||
rb_define_method(rb_mEnumerable, "all?", enum_all, -1);
|
rb_define_method(rb_mEnumerable, "all?", enum_all, -1);
|
||||||
rb_define_method(rb_mEnumerable, "any?", enum_any, -1);
|
rb_define_method(rb_mEnumerable, "any?", enum_any, -1);
|
||||||
|
|
|
@ -33,3 +33,31 @@ ruby_version_is "2.7" do
|
||||||
end
|
end
|
||||||
end
|
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
|
def test_tally
|
||||||
h = {1 => 2, 2 => 2, 3 => 1}
|
h = {1 => 2, 2 => 2, 3 => 1}
|
||||||
assert_equal(h, @obj.tally)
|
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
|
end
|
||||||
|
|
||||||
def test_first
|
def test_first
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue