класс Hash
A Хэш — это структура данных, подобная словарю, которая хранит уникальные ключи и соответствующие им значения. Также называется ассоциативным массивом. Подобно массивам, но вместо целых чисел в качестве индексов, хэш позволяет использовать любой тип объекта в качестве ключа.
Хэши перечисляют свои значения в порядке вставки соответствующих ключей.
Создать хэш можно с помощью неявной записи:
grades = { "Jane Doe" => 10, "Jim Doe" => 6 }
Если ключи всегда символы, можно использовать альтернативную запись. Вместо
options = { :font_size => 10, :font_family => "Arial" }
Можно записать так:
options = { font_size: 10, font_family: "Arial" }
Каждый именованный ключ — это символ, к которому можно обратиться в хэше:
options[:font_size] # => 10
A хэш также можно создать с помощью метода ::new:
grades = Hash.new grades["Dorothy Doe"] = 9
У хэша есть значение по умолчанию, которое возвращается при обращении к ключу, отсутствующему в хэше. Если значение по умолчанию не задано nil используется. Можно установить значение по умолчанию, передав его в качестве аргумента методу ::new:
grades = Hash.new(0)
Или с помощью метода default=:
grades = {"Timmy Doe" => 8}
grades.default = 0
Для доступа к значению в хэше необходимо указать его ключ:
puts grades["Jane Doe"] # => 0
Общие применения
Хэши — удобный способ представления структур данных, таких как
books = {}
books[:matz] = "The Ruby Language"
books[:black] = "The Well-Grounded Rubyist"
Хэши также часто используются для передачи именованных параметров в функции. Обратите внимание, что ниже не используются скобки. Если хэш является последним аргументом вызова метода, фигурные скобки не нужны, что создает действительно чистый интерфейс:
Person.create(name: "John Doe", age: 27) def self.create(params) @name = params[:name] @age = params[:age] end
Ключи хэша
Два объекта представляют один и тот же ключ хэша, когда их значения hash идентичны, и эти два объекта eql? друг другу.
Пользовательский класс может использоваться в качестве ключа хэша, если методы hash и eql? переопределены для обеспечения осмысленного поведения. По умолчанию отдельные экземпляры ссылаются на отдельные ключи хэша.
Типичная реализация hash основана на данных объекта, а eql? обычно является псевдонимом переопределенного метода ==:
class Book
attr_reader :author, :title
def initialize(author, title)
@author = author
@title = title
end
def ==(other)
self.class === other and
other.author == @author and
other.title == @title
end
alias eql? ==
def hash
@author.hash ^ @title.hash # XOR
end
end
book1 = Book.new 'matz', 'Ruby in a Nutshell'
book2 = Book.new 'matz', 'Ruby in a Nutshell'
reviews = {}
reviews[book1] = 'Great reference!'
reviews[book2] = 'Nice and compact!'
reviews.length #=> 1
См. также Object#hash и Object#eql?
Публичные методы класса
static VALUE
rb_hash_s_create(int argc, VALUE *argv, VALUE klass)
{
VALUE hash, tmp;
int i;
if (argc == 1) {
tmp = rb_hash_s_try_convert(Qnil, argv[0]);
if (!NIL_P(tmp)) {
hash = hash_alloc(klass);
if (RHASH(tmp)->ntbl) {
RHASH(hash)->ntbl = st_copy(RHASH(tmp)->ntbl);
}
return hash;
}
tmp = rb_check_array_type(argv[0]);
if (!NIL_P(tmp)) {
long i;
hash = hash_alloc(klass);
for (i = 0; i < RARRAY_LEN(tmp); ++i) {
VALUE e = RARRAY_AREF(tmp, i);
VALUE v = rb_check_array_type(e);
VALUE key, val = Qnil;
if (NIL_P(v)) {
#if 0 /* refix in the next release */
rb_raise(rb_eArgError, "wrong element type %s at %ld (expected array)",
rb_builtin_class_name(e), i);
#else
rb_warn("wrong element type %s at %ld (expected array)",
rb_builtin_class_name(e), i);
rb_warn("ignoring wrong elements is deprecated, remove them explicitly");
rb_warn("this causes ArgumentError in the next release");
continue;
#endif
}
switch (RARRAY_LEN(v)) {
default:
rb_raise(rb_eArgError, "invalid number of elements (%ld for 1..2)",
RARRAY_LEN(v));
case 2:
val = RARRAY_AREF(v, 1);
case 1:
key = RARRAY_AREF(v, 0);
rb_hash_aset(hash, key, val);
}
}
return hash;
}
}
if (argc % 2 != 0) {
rb_raise(rb_eArgError, "odd number of arguments for Hash");
}
hash = hash_alloc(klass);
for (i=0; i<argc; i+=2) {
rb_hash_aset(hash, argv[i], argv[i + 1]);
}
return hash;
} Создаёт новый хэш, заполненный указанными объектами.
Аналогично литералу { key => value, ...
}. В первой форме ключи и значения задаются парами, поэтому количество аргументов должно быть чётным.
Во второй и третьей форме используется один аргумент, который является либо массивом пар ключ-значение, либо объектом, преобразуемым в хэш.
Hash["a", 100, "b", 200] #=> {"a"=>100, "b"=>200}
Hash[ [ ["a", 100], ["b", 200] ] ] #=> {"a"=>100, "b"=>200}
Hash["a" => 100, "b" => 200] #=> {"a"=>100, "b"=>200}
static VALUE
rb_hash_initialize(int argc, VALUE *argv, VALUE hash)
{
VALUE ifnone;
rb_hash_modify(hash);
if (rb_block_given_p()) {
rb_check_arity(argc, 0, 0);
ifnone = rb_block_proc();
default_proc_arity_check(ifnone);
RHASH_SET_IFNONE(hash, ifnone);
FL_SET(hash, HASH_PROC_DEFAULT);
}
else {
rb_check_arity(argc, 0, 1);
ifnone = argc == 0 ? Qnil : argv[0];
RHASH_SET_IFNONE(hash, ifnone);
}
return hash;
} Возвращает новый пустой хэш. Если к этому хэшу обращаются по ключу, который не соответствует записи хэша, возвращаемое значение зависит от стиля new создания хэша. В первой форме доступ возвращает nil. Если указан obj, этот единственный объект будет использоваться для всех значений по умолчанию. Если указан блок, он будет вызван с объектом хэша и ключом, и должен вернуть значение по умолчанию. Блок несет ответственность за сохранение значения в хэше, если это необходимо.
h = Hash.new("Go Fish")
h["a"] = 100
h["b"] = 200
h["a"] #=> 100
h["c"] #=> "Go Fish"
# The following alters the single default object
h["c"].upcase! #=> "GO FISH"
h["d"] #=> "GO FISH"
h.keys #=> ["a", "b"]
# While this creates a new default object each time
h = Hash.new { |hash, key| hash[key] = "Go Fish: #{key}" }
h["c"] #=> "Go Fish: c"
h["c"].upcase! #=> "GO FISH: C"
h["d"] #=> "Go Fish: d"
h.keys #=> ["c", "d"]
static VALUE
rb_hash_s_try_convert(VALUE dummy, VALUE hash)
{
return rb_check_hash_type(hash);
} Попытка преобразовать obj в хэш, используя метод #to_hash. Возвращает преобразованный хэш или nil, если obj не может быть преобразован по какой-либо причине.
Hash.try_convert({1=>2}) # => {1=>2}
Hash.try_convert("1=>2") # => nil
Открытые методы экземпляра
static VALUE
rb_hash_equal(VALUE hash1, VALUE hash2)
{
return hash_equal(hash1, hash2, FALSE);
} Равенство—Два хэша равны, если каждый из них содержит одинаковое количество ключей и если каждая пара ключ-значение равна (в соответствии с Object#==) соответствующим элементам в другом хэше.
h1 = { "a" => 1, "c" => 2 }
h2 = { 7 => 35, "c" => 2, "a" => 1 }
h3 = { "a" => 1, "c" => 2, 7 => 35 }
h4 = { "a" => 1, "d" => 2, "f" => 35 }
h1 == h2 #=> false
h2 == h3 #=> true
h3 == h4 #=> false
VALUE
rb_hash_aref(VALUE hash, VALUE key)
{
st_data_t val;
if (!RHASH(hash)->ntbl || !st_lookup(RHASH(hash)->ntbl, key, &val)) {
return hash_default_value(hash, key);
}
return (VALUE)val;
} Ссылка на элемент—Извлекает объект value, соответствующий объекту key. Если не найден, возвращает значение по умолчанию (см. Hash::new для подробностей).
h = { "a" => 100, "b" => 200 }
h["a"] #=> 100
h["c"] #=> nil
VALUE
rb_hash_aset(VALUE hash, VALUE key, VALUE val)
{
int iter_lev = RHASH_ITER_LEV(hash);
st_table *tbl = RHASH(hash)->ntbl;
rb_hash_modify(hash);
if (!tbl) {
if (iter_lev > 0) no_new_key();
tbl = hash_tbl(hash);
}
if (tbl->type == &identhash || rb_obj_class(key) != rb_cString) {
RHASH_UPDATE_ITER(hash, iter_lev, key, hash_aset, val);
}
else {
RHASH_UPDATE_ITER(hash, iter_lev, key, hash_aset_str, val);
}
return val;
} Присваивание элемента
Связывает значение, заданное value, с ключом, заданным key.
h = { "a" => 100, "b" => 200 }
h["a"] = 9
h["c"] = 4
h #=> {"a"=>9, "b"=>200, "c"=>4}
h.store("d", 42) #=> 42
h #=> {"a"=>9, "b"=>200, "c"=>4, "d"=>42}
key не должно изменять свое значение, пока оно используется в качестве ключа (unfrozen String, переданный в качестве ключа, будет продублирован и заморожен).
a = "a"
b = "b".freeze
h = { a => 100, b => 200 }
h.key(100).equal? a #=> false
h.key(200).equal? b #=> true
static VALUE
rb_hash_any_p(VALUE hash)
{
VALUE ret = Qfalse;
if (RHASH_EMPTY_P(hash)) return Qfalse;
if (!rb_block_given_p()) {
/* yields pairs, never false */
return Qtrue;
}
if (rb_block_arity() > 1)
rb_hash_foreach(hash, any_p_i_fast, (VALUE)&ret);
else
rb_hash_foreach(hash, any_p_i, (VALUE)&ret);
return ret;
} См. также Enumerable#any?
VALUE
rb_hash_assoc(VALUE hash, VALUE key)
{
st_table *table;
const struct st_hash_type *orighash;
VALUE args[2];
if (RHASH_EMPTY_P(hash)) return Qnil;
table = RHASH(hash)->ntbl;
orighash = table->type;
if (orighash != &identhash) {
VALUE value;
struct reset_hash_type_arg ensure_arg;
struct st_hash_type assochash;
assochash.compare = assoc_cmp;
assochash.hash = orighash->hash;
table->type = &assochash;
args[0] = hash;
args[1] = key;
ensure_arg.hash = hash;
ensure_arg.orighash = orighash;
value = rb_ensure(lookup2_call, (VALUE)&args, reset_hash_type, (VALUE)&ensure_arg);
if (value != Qundef) return rb_assoc_new(key, value);
}
args[0] = key;
args[1] = Qnil;
rb_hash_foreach(hash, assoc_i, (VALUE)args);
return args[1];
} Производит поиск в хэше, сравнивая obj с ключом с помощью ==. Возвращает пару ключ-значение (массив из двух элементов) или nil, если совпадение не найдено. См. Array#assoc.
h = {"colors" => ["red", "blue", "green"],
"letters" => ["a", "b", "c" ]}
h.assoc("letters") #=> ["letters", ["a", "b", "c"]]
h.assoc("foo") #=> nil
VALUE
rb_hash_clear(VALUE hash)
{
rb_hash_modify_check(hash);
if (!RHASH(hash)->ntbl)
return hash;
if (RHASH(hash)->ntbl->num_entries > 0) {
if (RHASH_ITER_LEV(hash) > 0)
rb_hash_foreach(hash, clear_i, 0);
else
st_clear(RHASH(hash)->ntbl);
}
return hash;
} Удаляет все пары ключ-значение из hsh.
h = { "a" => 100, "b" => 200 } #=> {"a"=>100, "b"=>200}
h.clear #=> {}
static VALUE
rb_hash_compare_by_id(VALUE hash)
{
if (rb_hash_compare_by_id_p(hash)) return hash;
rb_hash_modify(hash);
RHASH(hash)->ntbl->type = &identhash;
rb_hash_rehash(hash);
return hash;
} Заставляет hsh сравнивать свои ключи по их идентичности, т. е. он будет считать точно такие же объекты одинаковыми ключами.
h1 = { "a" => 100, "b" => 200, :c => "c" }
h1["a"] #=> 100
h1.compare_by_identity
h1.compare_by_identity? #=> true
h1["a".dup] #=> nil # different objects.
h1[:c] #=> "c" # same symbols are all same.
VALUE
rb_hash_compare_by_id_p(VALUE hash)
{
if (!RHASH(hash)->ntbl)
return Qfalse;
if (RHASH(hash)->ntbl->type == &identhash) {
return Qtrue;
}
return Qfalse;
} Возвращает true, если hsh будет сравнивать свои ключи по их идентичности. См. также Hash#compare_by_identity.
static VALUE
rb_hash_default(int argc, VALUE *argv, VALUE hash)
{
VALUE key, ifnone;
rb_check_arity(argc, 0, 1);
key = argv[0];
ifnone = RHASH_IFNONE(hash);
if (FL_TEST(hash, HASH_PROC_DEFAULT)) {
if (argc == 0) return Qnil;
return rb_funcall(ifnone, id_yield, 2, hash, key);
}
return ifnone;
} Возвращает значение по умолчанию, значение, которое возвращалось бы hsh, если бы key не существовал в hsh. См. также Hash::new и Hash#default=.
h = Hash.new #=> {}
h.default #=> nil
h.default(2) #=> nil
h = Hash.new("cat") #=> {}
h.default #=> "cat"
h.default(2) #=> "cat"
h = Hash.new {|h,k| h[k] = k.to_i*10} #=> {}
h.default #=> nil
h.default(2) #=> 20
static VALUE
rb_hash_set_default(VALUE hash, VALUE ifnone)
{
rb_hash_modify_check(hash);
RHASH_SET_IFNONE(hash, ifnone);
FL_UNSET(hash, HASH_PROC_DEFAULT);
return ifnone;
} Устанавливает значение по умолчанию, значение, возвращаемое для ключа, который не существует в хэше. Невозможно установить значение по умолчанию в Proc, который будет выполняться при каждом поиске ключа.
h = { "a" => 100, "b" => 200 }
h.default = "Go fish"
h["a"] #=> 100
h["z"] #=> "Go fish"
# This doesn't do what you might hope...
h.default = proc do |hash, key|
hash[key] = key + key
end
h[2] #=> #<Proc:0x401b3948@-:6>
h["cat"] #=> #<Proc:0x401b3948@-:6>
static VALUE
rb_hash_default_proc(VALUE hash)
{
if (FL_TEST(hash, HASH_PROC_DEFAULT)) {
return RHASH_IFNONE(hash);
}
return Qnil;
} Если Hash::new был вызван с блоком, вернуть этот блок, иначе вернуть nil.
h = Hash.new {|h,k| h[k] = k*k } #=> {}
p = h.default_proc #=> #<Proc:0x401b3d08@-:1>
a = [] #=> []
p.call(a, 2)
a #=> [nil, nil, 4]
VALUE
rb_hash_set_default_proc(VALUE hash, VALUE proc)
{
VALUE b;
rb_hash_modify_check(hash);
if (NIL_P(proc)) {
FL_UNSET(hash, HASH_PROC_DEFAULT);
RHASH_SET_IFNONE(hash, proc);
return proc;
}
b = rb_check_convert_type(proc, T_DATA, "Proc", "to_proc");
if (NIL_P(b) || !rb_obj_is_proc(b)) {
rb_raise(rb_eTypeError,
"wrong default_proc type %s (expected Proc)",
rb_obj_classname(proc));
}
proc = b;
default_proc_arity_check(proc);
RHASH_SET_IFNONE(hash, proc);
FL_SET(hash, HASH_PROC_DEFAULT);
return proc;
} Устанавливает процедуру по умолчанию, которая будет выполняться при каждом неудачном поиске ключа.
h.default_proc = proc do |hash, key| hash[key] = key + key end h[2] #=> 4 h["cat"] #=> "catcat"
static VALUE
rb_hash_delete_m(VALUE hash, VALUE key)
{
VALUE val;
rb_hash_modify_check(hash);
val = rb_hash_delete_entry(hash, key);
if (val != Qundef) {
return val;
}
else {
if (rb_block_given_p()) {
return rb_yield(key);
}
else {
return Qnil;
}
}
} Удаляет пару ключ-значение и возвращает значение из hsh, ключ которого равен key. Если ключ не найден, возвращает значение по умолчанию. Если задан необязательный блок кода и ключ не найден, передайте ключ и верните результат блока.
h = { "a" => 100, "b" => 200 }
h.delete("a") #=> 100
h.delete("z") #=> nil
h.delete("z") { |el| "#{el} not found" } #=> "z not found"
VALUE
rb_hash_delete_if(VALUE hash)
{
RETURN_SIZED_ENUMERATOR(hash, 0, 0, hash_enum_size);
rb_hash_modify_check(hash);
if (RHASH(hash)->ntbl)
rb_hash_foreach(hash, delete_if_i, hash);
return hash;
} Удаляет каждую пару ключ-значение из hsh, для которой блок вычисляется в true.
Если блок не задан, вместо этого возвращается перечислитель.
h = { "a" => 100, "b" => 200, "c" => 300 }
h.delete_if {|key, value| key >= "b" } #=> {"a"=>100}
static VALUE
rb_hash_each_pair(VALUE hash)
{
RETURN_SIZED_ENUMERATOR(hash, 0, 0, hash_enum_size);
if (rb_block_arity() > 1)
rb_hash_foreach(hash, each_pair_i_fast, 0);
else
rb_hash_foreach(hash, each_pair_i, 0);
return hash;
} Вызывает блок один раз для каждого ключа в hsh, передавая пару ключ-значение в качестве параметров.
Если блок не задан, вместо этого возвращается перечислитель.
h = { "a" => 100, "b" => 200 }
h.each {|key, value| puts "#{key} is #{value}" }
выдает:
a is 100 b is 200
static VALUE
rb_hash_each_key(VALUE hash)
{
RETURN_SIZED_ENUMERATOR(hash, 0, 0, hash_enum_size);
rb_hash_foreach(hash, each_key_i, 0);
return hash;
} Вызывает блок один раз для каждого ключа в hsh, передавая ключ в качестве параметра.
Если блок не задан, вместо этого возвращается перечислитель.
h = { "a" => 100, "b" => 200 }
h.each_key {|key| puts key }
выдает:
a b
static VALUE
rb_hash_each_pair(VALUE hash)
{
RETURN_SIZED_ENUMERATOR(hash, 0, 0, hash_enum_size);
if (rb_block_arity() > 1)
rb_hash_foreach(hash, each_pair_i_fast, 0);
else
rb_hash_foreach(hash, each_pair_i, 0);
return hash;
} Вызывает блок один раз для каждого ключа в hsh, передавая пару ключ-значение в качестве параметров.
Если блок не задан, вместо этого возвращается перечислитель.
h = { "a" => 100, "b" => 200 }
h.each {|key, value| puts "#{key} is #{value}" }
выдает:
a is 100 b is 200
static VALUE
rb_hash_each_value(VALUE hash)
{
RETURN_SIZED_ENUMERATOR(hash, 0, 0, hash_enum_size);
rb_hash_foreach(hash, each_value_i, 0);
return hash;
} Вызывает блок один раз для каждого ключа в hsh, передавая значение в качестве параметра.
Если блок не задан, вместо этого возвращается перечислитель.
h = { "a" => 100, "b" => 200 }
h.each_value {|value| puts value }
выдает:
100 200
static VALUE
rb_hash_empty_p(VALUE hash)
{
return RHASH_EMPTY_P(hash) ? Qtrue : Qfalse;
} Возвращает true, если hsh не содержит пар ключ-значение.
{}.empty? #=> true
static VALUE
rb_hash_eql(VALUE hash1, VALUE hash2)
{
return hash_equal(hash1, hash2, TRUE);
} Возвращает true, если hash и other являются хэшами с одинаковым содержимым.
static VALUE
rb_hash_fetch_m(int argc, VALUE *argv, VALUE hash)
{
VALUE key;
st_data_t val;
long block_given;
rb_check_arity(argc, 1, 2);
key = argv[0];
block_given = rb_block_given_p();
if (block_given && argc == 2) {
rb_warn("block supersedes default value argument");
}
if (!RHASH(hash)->ntbl || !st_lookup(RHASH(hash)->ntbl, key, &val)) {
if (block_given) return rb_yield(key);
if (argc == 1) {
volatile VALUE desc = rb_protect(rb_inspect, key, 0);
if (NIL_P(desc)) {
desc = rb_any_to_s(key);
}
desc = rb_str_ellipsize(desc, 65);
rb_raise(rb_eKeyError, "key not found: %"PRIsVALUE, desc);
}
return argv[1];
}
return (VALUE)val;
} Возвращает значение из хэша для заданного ключа. Если ключ не найден, есть несколько вариантов: без других аргументов будет выброшено исключение KeyError; если задано значение default, то оно будет возвращено; если указан необязательный блок кода, то он будет выполнен и его результат будет возвращен.
h = { "a" => 100, "b" => 200 }
h.fetch("a") #=> 100
h.fetch("z", "go fish") #=> "go fish"
h.fetch("z") { |el| "go fish, #{el}"} #=> "go fish, z"
В следующем примере показано, что исключение возникает, если ключ не найден, и значение по умолчанию не указано.
h = { "a" => 100, "b" => 200 }
h.fetch("z")
produces:
prog.rb:2:in `fetch': key not found (KeyError) from prog.rb:2
static VALUE
rb_hash_flatten(int argc, VALUE *argv, VALUE hash)
{
VALUE ary;
if (argc) {
int level = NUM2INT(*argv);
if (level == 0) return rb_hash_to_a(hash);
ary = rb_ary_new_capa(RHASH_SIZE(hash) * 2);
rb_hash_foreach(hash, flatten_i, ary);
if (level - 1 > 0) {
*argv = INT2FIX(level - 1);
rb_funcall2(ary, id_flatten_bang, argc, argv);
}
else if (level < 0) {
rb_funcall2(ary, id_flatten_bang, 0, 0);
}
}
else {
ary = rb_ary_new_capa(RHASH_SIZE(hash) * 2);
rb_hash_foreach(hash, flatten_i, ary);
}
return ary;
} Возвращает новый массив, являющийся одномерным сглаживанием этого хэша. То есть для каждого ключа или значения, являющегося массивом, извлекает его элементы в новый массив. В отличие от Array#flatten, этот метод по умолчанию не выполняет рекурсивное сглаживание. Необязательный аргумент level определяет уровень рекурсии для сглаживания.
a = {1=> "one", 2 => [2,"two"], 3 => "three"}
a.flatten # => [1, "one", 2, [2, "two"], 3, "three"]
a.flatten(2) # => [1, "one", 2, 2, "two", 3, "three"]
VALUE
rb_hash_has_key(VALUE hash, VALUE key)
{
if (!RHASH(hash)->ntbl)
return Qfalse;
if (st_lookup(RHASH(hash)->ntbl, key, 0)) {
return Qtrue;
}
return Qfalse;
} Возвращает true, если заданный ключ присутствует в hsh.
h = { "a" => 100, "b" => 200 }
h.has_key?("a") #=> true
h.has_key?("z") #=> false
static VALUE
rb_hash_has_value(VALUE hash, VALUE val)
{
VALUE data[2];
data[0] = Qfalse;
data[1] = val;
rb_hash_foreach(hash, rb_hash_search_value, (VALUE)data);
return data[0];
} Возвращает true, если заданное значение присутствует для некоторого ключа в hsh.
h = { "a" => 100, "b" => 200 }
h.has_value?(100) #=> true
h.has_value?(999) #=> false
static VALUE
rb_hash_hash(VALUE hash)
{
st_index_t size = RHASH_SIZE(hash);
st_index_t hval = rb_hash_start(size);
hval = rb_hash_uint(hval, (st_index_t)rb_hash_hash);
if (size) {
rb_hash_foreach(hash, hash_i, (VALUE)&hval);
}
hval = rb_hash_end(hval);
return INT2FIX(hval);
} Вычисляет хэш-код для этого хэша. Два хэша с одинаковым содержимым будут иметь одинаковый хэш-код (и будут сравниваться с использованием eql?).
См. также Object#hash.
VALUE
rb_hash_has_key(VALUE hash, VALUE key)
{
if (!RHASH(hash)->ntbl)
return Qfalse;
if (st_lookup(RHASH(hash)->ntbl, key, 0)) {
return Qtrue;
}
return Qfalse;
} Возвращает true, если заданный ключ присутствует в hsh.
h = { "a" => 100, "b" => 200 }
h.has_key?("a") #=> true
h.has_key?("z") #=> false
static VALUE
rb_hash_inspect(VALUE hash)
{
if (RHASH_EMPTY_P(hash))
return rb_usascii_str_new2("{}");
return rb_exec_recursive(inspect_hash, hash, 0);
} Возвращает содержимое этого хэша в виде строки.
h = { "c" => 300, "a" => 100, "d" => 400, "c" => 300 }
h.to_s #=> "{\"c\"=>300, \"a\"=>100, \"d\"=>400}"
static VALUE
rb_hash_invert(VALUE hash)
{
VALUE h = rb_hash_new();
rb_hash_foreach(hash, rb_hash_invert_i, h);
return h;
} Возвращает новый хэш, созданный с использованием значений hsh в качестве ключей, а ключей — в качестве значений.
h = { "n" => 100, "m" => 100, "y" => 300, "d" => 200, "a" => 0 }
h.invert #=> {0=>"a", 100=>"m", 200=>"d", 300=>"y"}
VALUE
rb_hash_keep_if(VALUE hash)
{
RETURN_SIZED_ENUMERATOR(hash, 0, 0, hash_enum_size);
rb_hash_modify_check(hash);
if (RHASH(hash)->ntbl)
rb_hash_foreach(hash, keep_if_i, hash);
return hash;
} Удаляет каждую пару ключ-значение из hsh, для которой block возвращает false.
Если блок не задан, вместо этого возвращается перечислитель.
static VALUE
rb_hash_key(VALUE hash, VALUE value)
{
VALUE args[2];
args[0] = value;
args[1] = Qnil;
rb_hash_foreach(hash, key_i, (VALUE)args);
return args[1];
} Возвращает ключ вхождения данного значения. Если значение не найдено, возвращает nil.
h = { "a" => 100, "b" => 200, "c" => 300, "d" => 300 }
h.key(200) #=> "b"
h.key(300) #=> "c"
h.key(999) #=> nil
VALUE
rb_hash_has_key(VALUE hash, VALUE key)
{
if (!RHASH(hash)->ntbl)
return Qfalse;
if (st_lookup(RHASH(hash)->ntbl, key, 0)) {
return Qtrue;
}
return Qfalse;
} Возвращает true, если заданный ключ присутствует в hsh.
h = { "a" => 100, "b" => 200 }
h.has_key?("a") #=> true
h.has_key?("z") #=> false
VALUE
rb_hash_keys(VALUE hash)
{
VALUE keys;
st_index_t size = RHASH_SIZE(hash);
keys = rb_ary_new_capa(size);
if (size == 0) return keys;
if (ST_DATA_COMPATIBLE_P(VALUE)) {
st_table *table = RHASH(hash)->ntbl;
rb_gc_writebarrier_remember(keys);
RARRAY_PTR_USE(keys, ptr, {
size = st_keys_check(table, ptr, size, Qundef);
});
rb_ary_set_len(keys, size);
}
else {
rb_hash_foreach(hash, keys_i, keys);
}
return keys;
} Возвращает новый массив, заполненный ключами из этого хэша. См. также Hash#values.
h = { "a" => 100, "b" => 200, "c" => 300, "d" => 400 }
h.keys #=> ["a", "b", "c", "d"]
VALUE
rb_hash_size(VALUE hash)
{
return INT2FIX(RHASH_SIZE(hash));
} Возвращает количество пар ключ-значение в хэше.
h = { "d" => 100, "a" => 200, "v" => 300, "e" => 400 }
h.length #=> 4
h.delete("a") #=> 200
h.length #=> 3
VALUE
rb_hash_has_key(VALUE hash, VALUE key)
{
if (!RHASH(hash)->ntbl)
return Qfalse;
if (st_lookup(RHASH(hash)->ntbl, key, 0)) {
return Qtrue;
}
return Qfalse;
} Возвращает true, если заданный ключ присутствует в hsh.
h = { "a" => 100, "b" => 200 }
h.has_key?("a") #=> true
h.has_key?("z") #=> false
static VALUE
rb_hash_merge(VALUE hash1, VALUE hash2)
{
return rb_hash_update(rb_obj_dup(hash1), hash2);
} Возвращает новый хэш, содержащий содержимое other_hash и содержимое hsh. Если блок не указан, значение для записей с дублирующимися ключами будет значением из other_hash. В противном случае значение для каждого дублирующегося ключа определяется вызовом блока с ключом, его значением в hsh и его значением в other_hash.
h1 = { "a" => 100, "b" => 200 }
h2 = { "b" => 254, "c" => 300 }
h1.merge(h2) #=> {"a"=>100, "b"=>254, "c"=>300}
h1.merge(h2){|key, oldval, newval| newval - oldval}
#=> {"a"=>100, "b"=>54, "c"=>300}
h1 #=> {"a"=>100, "b"=>200}
static VALUE
rb_hash_update(VALUE hash1, VALUE hash2)
{
rb_hash_modify(hash1);
hash2 = to_hash(hash2);
if (rb_block_given_p()) {
rb_hash_foreach(hash2, rb_hash_update_block_i, hash1);
}
else {
rb_hash_foreach(hash2, rb_hash_update_i, hash1);
}
return hash1;
} Добавляет содержимое other_hash к hsh. Если блок не указан, записи с дублирующимися ключами перезаписываются значениями из other_hash, в противном случае значение каждого дублирующегося ключа определяется вызовом блока с ключом, его значением в hsh и его значением в other_hash.
h1 = { "a" => 100, "b" => 200 }
h2 = { "b" => 254, "c" => 300 }
h1.merge!(h2) #=> {"a"=>100, "b"=>254, "c"=>300}
h1 = { "a" => 100, "b" => 200 }
h2 = { "b" => 254, "c" => 300 }
h1.merge!(h2) { |key, v1, v2| v1 }
#=> {"a"=>100, "b"=>200, "c"=>300}
VALUE
rb_hash_rassoc(VALUE hash, VALUE obj)
{
VALUE args[2];
args[0] = obj;
args[1] = Qnil;
rb_hash_foreach(hash, rassoc_i, (VALUE)args);
return args[1];
} Производит поиск в хэше, сравнивая obj со значением с использованием ==. Возвращает первую пару ключ-значение (массив из двух элементов), которая соответствует. См. также Array#rassoc.
a = {1=> "one", 2 => "two", 3 => "three", "ii" => "two"}
a.rassoc("two") #=> [2, "two"]
a.rassoc("four") #=> nil
static VALUE
rb_hash_rehash(VALUE hash)
{
VALUE tmp;
st_table *tbl;
if (RHASH_ITER_LEV(hash) > 0) {
rb_raise(rb_eRuntimeError, "rehash during iteration");
}
rb_hash_modify_check(hash);
if (!RHASH(hash)->ntbl)
return hash;
tmp = hash_alloc(0);
tbl = st_init_table_with_size(RHASH(hash)->ntbl->type, RHASH(hash)->ntbl->num_entries);
RHASH(tmp)->ntbl = tbl;
rb_hash_foreach(hash, rb_hash_rehash_i, (VALUE)tbl);
st_free_table(RHASH(hash)->ntbl);
RHASH(hash)->ntbl = tbl;
RHASH(tmp)->ntbl = 0;
return hash;
} Перестраивает хэш на основе текущих значений хэша для каждого ключа. Если значения объектов ключей изменились с момента их вставки, этот метод переиндексирует hsh. Если Hash#rehash вызывается, когда итератор проходит по хэшу, в итераторе будет возбуждено исключение RuntimeError.
a = [ "a", "b" ]
c = [ "c", "d" ]
h = { a => 100, c => 300 }
h[a] #=> 100
a[0] = "z"
h[a] #=> nil
h.rehash #=> {["z", "b"]=>100, ["c", "d"]=>300}
h[a] #=> 100
VALUE
rb_hash_reject(VALUE hash)
{
VALUE result;
RETURN_SIZED_ENUMERATOR(hash, 0, 0, hash_enum_size);
if (RTEST(ruby_verbose)) {
VALUE klass;
if (HAS_EXTRA_STATES(hash, klass)) {
rb_warn("extra states are no longer copied: %+"PRIsVALUE, hash);
}
}
result = rb_hash_new();
if (!RHASH_EMPTY_P(hash)) {
rb_hash_foreach(hash, reject_i, result);
}
return result;
} Возвращает новый хэш, состоящий из записей, для которых блок возвращает false.
Если блок не задан, вместо этого возвращается перечислитель.
h = { "a" => 100, "b" => 200, "c" => 300 }
h.reject {|k,v| k < "b"} #=> {"b" => 200, "c" => 300}
h.reject {|k,v| v > 100} #=> {"a" => 100}
VALUE
rb_hash_reject_bang(VALUE hash)
{
st_index_t n;
RETURN_SIZED_ENUMERATOR(hash, 0, 0, hash_enum_size);
rb_hash_modify(hash);
n = RHASH_SIZE(hash);
if (!n) return Qnil;
rb_hash_foreach(hash, delete_if_i, hash);
if (n == RHASH(hash)->ntbl->num_entries) return Qnil;
return hash;
} Эквивалентно Hash#delete_if, но возвращает nil, если изменения не были внесены.
static VALUE
rb_hash_replace(VALUE hash, VALUE hash2)
{
st_table *table2;
rb_hash_modify_check(hash);
if (hash == hash2) return hash;
hash2 = to_hash(hash2);
RHASH_SET_IFNONE(hash, RHASH_IFNONE(hash2));
if (FL_TEST(hash2, HASH_PROC_DEFAULT))
FL_SET(hash, HASH_PROC_DEFAULT);
else
FL_UNSET(hash, HASH_PROC_DEFAULT);
table2 = RHASH(hash2)->ntbl;
rb_hash_clear(hash);
if (table2) hash_tbl(hash)->type = table2->type;
rb_hash_foreach(hash2, replace_i, hash);
return hash;
} Заменяет содержимое hsh содержимым other_hash.
h = { "a" => 100, "b" => 200 }
h.replace({ "c" => 300, "d" => 400 }) #=> {"c"=>300, "d"=>400}
VALUE
rb_hash_select(VALUE hash)
{
VALUE result;
RETURN_SIZED_ENUMERATOR(hash, 0, 0, hash_enum_size);
result = rb_hash_new();
if (!RHASH_EMPTY_P(hash)) {
rb_hash_foreach(hash, select_i, result);
}
return result;
} Возвращает новый хэш, состоящий из пар ключ-значение, для которых блок возвращает true.
Если блок не задан, возвращается перечислитель.
h = { "a" => 100, "b" => 200, "c" => 300 }
h.select {|k,v| k > "a"} #=> {"b" => 200, "c" => 300}
h.select {|k,v| v < 200} #=> {"a" => 100}
VALUE
rb_hash_select_bang(VALUE hash)
{
st_index_t n;
RETURN_SIZED_ENUMERATOR(hash, 0, 0, hash_enum_size);
rb_hash_modify_check(hash);
if (!RHASH(hash)->ntbl)
return Qnil;
n = RHASH(hash)->ntbl->num_entries;
rb_hash_foreach(hash, keep_if_i, hash);
if (n == RHASH(hash)->ntbl->num_entries) return Qnil;
return hash;
} Эквивалентно Hash#keep_if, но возвращает nil если изменений не произошло.
static VALUE
rb_hash_shift(VALUE hash)
{
struct shift_var var;
rb_hash_modify_check(hash);
if (RHASH(hash)->ntbl) {
var.key = Qundef;
if (RHASH_ITER_LEV(hash) == 0) {
if (st_shift(RHASH(hash)->ntbl, &var.key, &var.val)) {
return rb_assoc_new(var.key, var.val);
}
}
else {
rb_hash_foreach(hash, shift_i_safe, (VALUE)&var);
if (var.key != Qundef) {
rb_hash_delete_entry(hash, var.key);
return rb_assoc_new(var.key, var.val);
}
}
}
return hash_default_value(hash, Qnil);
} Удаляет пару ключ-значение из hsh и возвращает её как двухэлементный массив [ ключ, значение ], или значение по умолчанию хэша, если хэш пуст.
h = { 1 => "a", 2 => "b", 3 => "c" }
h.shift #=> [1, "a"]
h #=> {2=>"b", 3=>"c"}
VALUE
rb_hash_size(VALUE hash)
{
return INT2FIX(RHASH_SIZE(hash));
} Возвращает количество пар ключ-значение в хэше.
h = { "d" => 100, "a" => 200, "v" => 300, "e" => 400 }
h.length #=> 4
h.delete("a") #=> 200
h.length #=> 3
VALUE
rb_hash_aset(VALUE hash, VALUE key, VALUE val)
{
int iter_lev = RHASH_ITER_LEV(hash);
st_table *tbl = RHASH(hash)->ntbl;
rb_hash_modify(hash);
if (!tbl) {
if (iter_lev > 0) no_new_key();
tbl = hash_tbl(hash);
}
if (tbl->type == &identhash || rb_obj_class(key) != rb_cString) {
RHASH_UPDATE_ITER(hash, iter_lev, key, hash_aset, val);
}
else {
RHASH_UPDATE_ITER(hash, iter_lev, key, hash_aset_str, val);
}
return val;
} Присваивание элемента
Связывает значение, заданное value, с ключом, заданным key.
h = { "a" => 100, "b" => 200 }
h["a"] = 9
h["c"] = 4
h #=> {"a"=>9, "b"=>200, "c"=>4}
h.store("d", 42) #=> 42
h #=> {"a"=>9, "b"=>200, "c"=>4, "d"=>42}
key не должен изменять своё значение во время использования в качестве ключа (ключ, являющийся unfrozen String будет продублирован и заморожен).
a = "a"
b = "b".freeze
h = { a => 100, b => 200 }
h.key(100).equal? a #=> false
h.key(200).equal? b #=> true
static VALUE
rb_hash_to_a(VALUE hash)
{
VALUE ary;
ary = rb_ary_new_capa(RHASH_SIZE(hash));
rb_hash_foreach(hash, to_a_i, ary);
OBJ_INFECT(ary, hash);
return ary;
} Преобразует hsh в вложенный массив [ массивов ключ, значение ].
h = { "c" => 300, "a" => 100, "d" => 400, "c" => 300 }
h.to_a #=> [["c", 300], ["a", 100], ["d", 400]]
static VALUE
rb_hash_to_h(VALUE hash)
{
if (rb_obj_class(hash) != rb_cHash) {
VALUE ret = rb_hash_new();
if (!RHASH_EMPTY_P(hash))
RHASH(ret)->ntbl = st_copy(RHASH(hash)->ntbl);
if (FL_TEST(hash, HASH_PROC_DEFAULT)) {
FL_SET(ret, HASH_PROC_DEFAULT);
}
RHASH_SET_IFNONE(ret, RHASH_IFNONE(hash));
return ret;
}
return hash;
} Возвращает self. Если вызывается на подклассе Hash, преобразует получателя в объект Hash.
static VALUE
rb_hash_to_hash(VALUE hash)
{
return hash;
} Возвращает self.
static VALUE
rb_hash_update(VALUE hash1, VALUE hash2)
{
rb_hash_modify(hash1);
hash2 = to_hash(hash2);
if (rb_block_given_p()) {
rb_hash_foreach(hash2, rb_hash_update_block_i, hash1);
}
else {
rb_hash_foreach(hash2, rb_hash_update_i, hash1);
}
return hash1;
} Добавляет содержимое other_hash в hsh. Если блок не указан, записи с дублирующими ключами перезаписываются значениями из other_hash, в противном случае значение каждого дублирующего ключа определяется вызовом блока с ключом, его значением в hsh и его значением в other_hash.
h1 = { "a" => 100, "b" => 200 }
h2 = { "b" => 254, "c" => 300 }
h1.merge!(h2) #=> {"a"=>100, "b"=>254, "c"=>300}
h1 = { "a" => 100, "b" => 200 }
h2 = { "b" => 254, "c" => 300 }
h1.merge!(h2) { |key, v1, v2| v1 }
#=> {"a"=>100, "b"=>200, "c"=>300}
static VALUE
rb_hash_has_value(VALUE hash, VALUE val)
{
VALUE data[2];
data[0] = Qfalse;
data[1] = val;
rb_hash_foreach(hash, rb_hash_search_value, (VALUE)data);
return data[0];
} Возвращает true если заданное значение присутствует для какого-то ключа в hsh.
h = { "a" => 100, "b" => 200 }
h.has_value?(100) #=> true
h.has_value?(999) #=> false
VALUE
rb_hash_values(VALUE hash)
{
VALUE values;
st_index_t size = RHASH_SIZE(hash);
values = rb_ary_new_capa(size);
if (size == 0) return values;
if (ST_DATA_COMPATIBLE_P(VALUE)) {
st_table *table = RHASH(hash)->ntbl;
rb_gc_writebarrier_remember(values);
RARRAY_PTR_USE(values, ptr, {
size = st_values_check(table, ptr, size, Qundef);
});
rb_ary_set_len(values, size);
}
else {
rb_hash_foreach(hash, values_i, values);
}
return values;
} Возвращает новый массив, заполненный значениями из hsh. Смотрите также Hash#keys.
h = { "a" => 100, "b" => 200, "c" => 300 }
h.values #=> [100, 200, 300]
VALUE
rb_hash_values_at(int argc, VALUE *argv, VALUE hash)
{
VALUE result = rb_ary_new2(argc);
long i;
for (i=0; i<argc; i++) {
rb_ary_push(result, rb_hash_aref(hash, argv[i]));
}
return result;
} Возвращает массив, содержащий значения, связанные с заданными ключами. Также см. Hash.select.
h = { "cat" => "feline", "dog" => "canine", "cow" => "bovine" }
h.values_at("cow", "cat") #=> ["bovine", "feline"]
Ruby Core © 1993–2017 Yukihiro Matsumoto
Licensed under the Ruby License.
Ruby Standard Library © contributors
Licensed under their own licenses.