класс Hash
Хэш сопоставляет каждый уникальный ключ со своим значением.
Хэш имеет некоторое сходство с массивом, но:
-
Индекс массива всегда является целым числом.
-
Ключом хэша может быть (почти) любой объект.
Синтаксис данных хэша
Старый синтаксис для данных хэша использует «стрелку хэша», =>:
h = {:foo => 0, :bar => 1, :baz => 2}
h # => {:foo=>0, :bar=>1, :baz=>2}
В качестве альтернативы, но только для ключа хэша, являющегося символом, можно использовать новый синтаксис в стиле JSON, где каждое слово становится символом:
h = {foo: 0, bar: 1, baz: 2}
h # => {:foo=>0, :bar=>1, :baz=>2}
Вы также можете использовать строку вместо слова:
h = {'foo': 0, 'bar': 1, 'baz': 2}
h # => {:foo=>0, :bar=>1, :baz=>2}
И вы можете смешивать стили:
h = {foo: 0, :bar => 1, 'baz': 2}
h # => {:foo=>0, :bar=>1, :baz=>2}
Однако, попытка использовать синтаксис в стиле JSON для ключа, который не является словом или строкой, является ошибкой:
# Raises SyntaxError (syntax error, unexpected ':', expecting =>):
h = {0: 'zero'} Общие применения
Вы можете использовать хэш для присвоения имен объектам:
person = {name: 'Matz', language: 'Ruby'}
person # => {:name=>"Matz", :language=>"Ruby"}
Вы можете использовать хэш для присвоения имен аргументам метода:
def some_method(hash)
p hash
end
some_method({foo: 0, bar: 1, baz: 2}) # => {:foo=>0, :bar=>1, :baz=>2}
Примечание: когда последним аргументом вызова метода является хэш, фигурные скобки можно опустить:
some_method(foo: 0, bar: 1, baz: 2) # => {:foo=>0, :bar=>1, :baz=>2}
Вы можете использовать хэш для инициализации объекта:
class Dev
attr_accessor :name, :language
def initialize(hash)
self.name = hash[:name]
self.language = hash[:language]
end
end
matz = Dev.new(name: 'Matz', language: 'Ruby')
matz # => #<Dev: @name="Matz", @language="Ruby">
Создание хэша
Вот три способа создания хэша:
-
Метод
Hash.new -
Метод
Hash[] -
Литеральная форма:
{}.
Вы можете создать хэш, вызвав метод Hash.new.
Создайте пустой хэш:
h = Hash.new
h # => {}
h.class # => Hash
Вы можете создать хэш, вызвав метод Hash.[].
Создайте пустой хэш:
h = Hash[]
h # => {}
Создайте хэш с начальными записями:
h = Hash[foo: 0, bar: 1, baz: 2]
h # => {:foo=>0, :bar=>1, :baz=>2}
Вы можете создать хэш, используя его литеральную форму (фигурные скобки).
Создайте пустой хэш:
h = {}
h # => {}
Создайте хэш с начальными записями:
h = {foo: 0, bar: 1, baz: 2}
h # => {:foo=>0, :bar=>1, :baz=>2}
Основы значений хэша
Простейший способ получения значения хэша (метод экземпляра []):
h = {foo: 0, bar: 1, baz: 2}
h[:foo] # => 0
Простейший способ создания или обновления значения хэша (метод экземпляра []=):
h = {foo: 0, bar: 1, baz: 2}
h[:bat] = 3 # => 3
h # => {:foo=>0, :bar=>1, :baz=>2, :bat=>3}
h[:foo] = 4 # => 4
h # => {:foo=>4, :bar=>1, :baz=>2, :bat=>3}
Простейший способ удаления записи из хэша (метод экземпляра delete):
h = {foo: 0, bar: 1, baz: 2}
h.delete(:bar) # => 1
h # => {:foo=>0, :baz=>2}
Порядок записей
Объект хэша представляет свои записи в порядке их создания. Это видно в:
-
Итерационных методах, таких как
each,each_key,each_pair,each_value. -
Других методах, чувствительных к порядку, таких как
shift,keys,values. -
Строке, возвращаемой методом
inspect.
Новый хэш имеет свой начальный порядок согласно заданным записям:
h = Hash[foo: 0, bar: 1]
h # => {:foo=>0, :bar=>1}
Новые записи добавляются в конец:
h[:baz] = 2
h # => {:foo=>0, :bar=>1, :baz=>2}
Обновление значения не влияет на порядок:
h[:baz] = 3
h # => {:foo=>0, :bar=>1, :baz=>3}
Но повторное создание удаленной записи может повлиять на порядок:
h.delete(:foo)
h[:foo] = 5
h # => {:bar=>1, :baz=>3, :foo=>5}
Ключи хэша
Эквивалентность ключей хэша
Два объекта считаются одинаковыми ключами хэша, когда их hash значение идентично, и два объекта eql? друг другу.
Изменение активного ключа хэша
Изменение ключа хэша во время его использования повреждает индекс хэша.
Этот хэш имеет ключи, которые являются массивами:
a0 = [ :foo, :bar ]
a1 = [ :baz, :bat ]
h = {a0 => 0, a1 => 1}
h.include?(a0) # => true
h[a0] # => 0
a0.hash # => 110002110
Изменение элемента массива a0[0] изменяет его значение хэша:
a0[0] = :bam a0.hash # => 1069447059
И повреждает индекс хэша:
h.include?(a0) # => false h[a0] # => nil
Вы можете исправить индекс хэша, используя метод rehash:
h.rehash # => {[:bam, :bar]=>0, [:baz, :bat]=>1}
h.include?(a0) # => true
h[a0] # => 0
Ключ-строка всегда безопасен. Это потому, что незамороженная строка, переданная в качестве ключа, будет заменена дублированной и замороженной строкой:
s = 'foo'
s.frozen? # => false
h = {s => 0}
first_key = h.keys.first
first_key.frozen? # => true
Пользовательские ключи хэша
Чтобы быть используемыми в качестве ключей хэша, объекты должны реализовывать методы hash и eql?. Примечание: это требование не применяется, если хэш использует compare_by_id, так как сравнение в этом случае будет основываться на идентификаторе объекта ключей вместо hash и eql?.
Объект определяет базовую реализацию для hash и eq? , что делает каждый объект уникальным ключом. Обычно пользовательские классы захотят переопределить эти методы, чтобы обеспечить осмысленное поведение, или, например, унаследовать Struct, который имеет полезные определения для них.
Типичная реализация hash основана на данных объекта, а eql? обычно является псевдонимом переопределенного метода ==:
class Book
attr_reader :author, :title
def initialize(author, title)
@author = author
@title = title
end
def ==(other)
self.class === other &&
other.author == @author &&
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
Значения по умолчанию
Методы [], values_at и dig должны возвращать значение, связанное с определенным ключом. Когда этот ключ не найден, это значение будет определено его проком по умолчанию (если есть) или же его значением по умолчанию (по умолчанию `nil`).
Вы можете получить значение по умолчанию с помощью метода default:
h = Hash.new h.default # => nil
Вы можете установить значение по умолчанию, передав аргумент методу Hash.new или с помощью метода default=
h = Hash.new(-1) h.default # => -1 h.default = 0 h.default # => 0
Это значение по умолчанию возвращается для [], values_at и dig, когда ключ не найден:
counts = {foo: 42}
counts.default # => nil (default)
counts[:foo] = 42
counts[:bar] # => nil
counts.default = 0
counts[:bar] # => 0
counts.values_at(:foo, :bar, :baz) # => [42, 0, 0]
counts.dig(:bar) # => 0
Обратите внимание, что значение по умолчанию используется без дублирования. Не рекомендуется устанавливать значение по умолчанию для изменяемого объекта:
synonyms = Hash.new([]) synonyms[:hello] # => [] synonyms[:hello] << :hi # => [:hi], but this mutates the default! synonyms.default # => [:hi] synonyms[:world] << :universe synonyms[:world] # => [:hi, :universe], oops synonyms.keys # => [], oops
Для использования изменяемого объекта в качестве значения по умолчанию рекомендуется использовать процедуру по умолчанию
Процедура по умолчанию
Когда процедура по умолчанию для хэша установлена (т. е., не nil), значение по умолчанию, возвращаемое методом [], определяется только процедурой по умолчанию.
Вы можете получить процедуру по умолчанию с помощью метода default_proc:
h = Hash.new h.default_proc # => nil
Вы можете установить процедуру по умолчанию, вызвав Hash.new с блоком или вызвав метод default_proc=
h = Hash.new { |hash, key| "Default value for #{key}" }
h.default_proc.class # => Proc
h.default_proc = proc { |hash, key| "Default value for #{key.inspect}" }
h.default_proc.class # => Proc
Когда процедура по умолчанию установлена (т. е., не nil) и метод [] вызывается с несуществующим ключом, [] вызывает процедуру по умолчанию с самим объектом хэша и отсутствующим ключом, а затем возвращает значение, возвращаемое процедурой:
h = Hash.new { |hash, key| "Default value for #{key}" }
h[:nosuch] # => "Default value for nosuch"
Обратите внимание, что в приведенном выше примере запись для ключа :nosuch не создаётся:
h.include?(:nosuch) # => false
Однако, сама процедура может добавить новую запись:
synonyms = Hash.new { |hash, key| hash[key] = [] }
synonyms.include?(:hello) # => false
synonyms[:hello] << :hi # => [:hi]
synonyms[:world] << :universe # => [:universe]
synonyms.keys # => [:hello, :world]
Обратите внимание, что установка процедуры по умолчанию очистит значение по умолчанию и наоборот.
Методы публичного класса
static VALUE
rb_hash_s_create(int argc, VALUE *argv, VALUE klass)
{
VALUE hash, tmp;
if (argc == 1) {
tmp = rb_hash_s_try_convert(Qnil, argv[0]);
if (!NIL_P(tmp)) {
hash = hash_alloc(klass);
hash_copy(hash, tmp);
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)) {
rb_raise(rb_eArgError, "wrong element type %s at %ld (expected array)",
rb_builtin_class_name(e), i);
}
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);
rb_hash_bulk_insert(argc, argv, hash);
hash_verify(hash);
return hash;
} Возвращает новый объект Hash, заполненный заданными объектами, если таковые имеются. См. Hash::new.
Без аргументов возвращает новый пустой Hash.
Если единственный аргумент — Hash, возвращает новый Hash, заполненный записями из переданного Hash.
h = {foo: 0, bar: 1, baz: 2}
Hash[h] # => {:foo=>0, :bar=>1, :baz=>2}
Если единственный аргумент — массив 2-элементных массивов, возвращает новый объект Hash, где каждый 2-элементный массив образует пару ключ-значение:
Hash[ [ [:foo, 0], [:bar, 1] ] ] # => {:foo=>0, :bar=>1}
Если количество аргументов чётное, возвращает новый объект Hash, где каждая пара последовательных аргументов образует пару ключ-значение:
Hash[:foo, 0, :bar, 1] # => {:foo=>0, :bar=>1}
Вызывает исключение, если список аргументов не соответствует ни одному из вышеперечисленных случаев.
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();
SET_PROC_DEFAULT(hash, ifnone);
}
else {
rb_check_arity(argc, 0, 1);
ifnone = argc == 0 ? Qnil : argv[0];
RHASH_SET_IFNONE(hash, ifnone);
}
return hash;
} Возвращает новый пустой объект Hash.
Начальное значение по умолчанию и начальный прок для нового хэша зависят от используемой формы. См. Значения по умолчанию.
Если ни аргумент, ни блок не заданы, то и значение по умолчанию, и прок по умолчанию устанавливаются в nil:
h = Hash.new h.default # => nil h.default_proc # => nil
Если аргумент default_value задан, но блок нет, значение по умолчанию устанавливается в переданное default_value, а прок по умолчанию в nil:
h = Hash.new(false) h.default # => false h.default_proc # => nil
Если задан блок, но нет аргументов, то блок устанавливается в качестве прока по умолчанию, а значение по умолчанию устанавливается в nil:
h = Hash.new {|hash, key| "Default value for #{key}" }
h.default # => nil
h.default_proc.class # => Proc
h[:nosuch] # => "Default value for nosuch"
static VALUE
rb_hash_s_ruby2_keywords_hash(VALUE dummy, VALUE hash)
{
Check_Type(hash, T_HASH);
hash = rb_hash_dup(hash);
RHASH(hash)->basic.flags |= RHASH_PASS_AS_KEYWORDS;
return hash;
} Создаёт копию заданного хэша и добавляет флаг ruby2_keywords. Этот метод не предназначен для повседневного использования; он может быть полезен для отладки, исследования и некоторых действительно необходимых случаев, таких как десериализация аргументов.
h = {k: 1}
h = Hash.ruby2_keywords_hash(h)
def foo(k: 42)
k
end
foo(*[h]) #=> 1 with neither a warning or an error
static VALUE
rb_hash_s_ruby2_keywords_hash_p(VALUE dummy, VALUE hash)
{
Check_Type(hash, T_HASH);
return (RHASH(hash)->basic.flags & RHASH_PASS_AS_KEYWORDS) ? Qtrue : Qfalse;
} Проверяет, помечен ли заданный хэш флагом Module#ruby2_keywords (или Proc#ruby2_keywords). Этот метод не предназначен для повседневного использования; он может быть полезен для отладки, исследования и некоторых действительно необходимых случаев, таких как сериализация аргументов.
ruby2_keywords def foo(*args)
Hash.ruby2_keywords_hash?(args.last)
end
foo(k: 1) #=> true
foo({k: 1}) #=> false
static VALUE
rb_hash_s_try_convert(VALUE dummy, VALUE hash)
{
return rb_check_hash_type(hash);
} Если obj является объектом Hash, возвращает obj.
В противном случае, если obj отвечает на :to_hash, вызывает obj.to_hash и возвращает результат.
Возвращает nil если obj не отвечает на :to_hash
Вызывает исключение, если obj.to_hash не возвращает объект Hash.
Методы публичного экземпляра
static VALUE
rb_hash_lt(VALUE hash, VALUE other)
{
other = to_hash(other);
if (RHASH_SIZE(hash) >= RHASH_SIZE(other)) return Qfalse;
return hash_le(hash, other);
} Возвращает true, если hash является правильным подмножеством other_hash, в противном случае — false:
h1 = {foo: 0, bar: 1}
h2 = {foo: 0, bar: 1, baz: 2}
h1 < h2 # => true
h2 < h1 # => false
h1 < h1 # => false
static VALUE
rb_hash_le(VALUE hash, VALUE other)
{
other = to_hash(other);
if (RHASH_SIZE(hash) > RHASH_SIZE(other)) return Qfalse;
return hash_le(hash, other);
} Возвращает true, если hash является подмножеством other_hash, в противном случае — false:
h1 = {foo: 0, bar: 1}
h2 = {foo: 0, bar: 1, baz: 2}
h1 <= h2 # => true
h2 <= h1 # => false
h1 <= h1 # => true
static VALUE
rb_hash_equal(VALUE hash1, VALUE hash2)
{
return hash_equal(hash1, hash2, FALSE);
} Возвращает true, если все перечисленные условия верны:
-
objectявляется объектом Hash. -
hashиobjectимеют одинаковые ключи (независимо от порядка). -
Для каждого ключа
key,hash[key] == object[key].
В противном случае возвращает false.
Равны:
h1 = {foo: 0, bar: 1, baz: 2}
h2 = {foo: 0, bar: 1, baz: 2}
h1 == h2 # => true
h3 = {baz: 2, bar: 1, foo: 0}
h1 == h3 # => true
static VALUE
rb_hash_gt(VALUE hash, VALUE other)
{
other = to_hash(other);
if (RHASH_SIZE(hash) <= RHASH_SIZE(other)) return Qfalse;
return hash_le(other, hash);
} Возвращает true, если hash является правильным надмножеством other_hash, в противном случае — false:
h1 = {foo: 0, bar: 1, baz: 2}
h2 = {foo: 0, bar: 1}
h1 > h2 # => true
h2 > h1 # => false
h1 > h1 # => false
static VALUE
rb_hash_ge(VALUE hash, VALUE other)
{
other = to_hash(other);
if (RHASH_SIZE(hash) < RHASH_SIZE(other)) return Qfalse;
return hash_le(other, hash);
} Возвращает true, если hash является надмножеством other_hash, в противном случае — false:
h1 = {foo: 0, bar: 1, baz: 2}
h2 = {foo: 0, bar: 1}
h1 >= h2 # => true
h2 >= h1 # => false
h1 >= h1 # => true
VALUE
rb_hash_aref(VALUE hash, VALUE key)
{
st_data_t val;
if (hash_stlike_lookup(hash, key, &val)) {
return (VALUE)val;
}
else {
return rb_hash_default_value(hash, key);
}
} Возвращает значение, связанное с заданным ключом, если он найден:
h = {foo: 0, bar: 1, baz: 2}
h[:foo] # => 0
Если ключ key не найден, возвращает значение по умолчанию (см. Значения по умолчанию):
h = {foo: 0, bar: 1, baz: 2}
h[:nosuch] # => nil
VALUE
rb_hash_aset(VALUE hash, VALUE key, VALUE val)
{
int iter_lev = RHASH_ITER_LEV(hash);
rb_hash_modify(hash);
if (RHASH_TABLE_NULL_P(hash)) {
if (iter_lev > 0) no_new_key();
ar_alloc_table(hash);
}
if (RHASH_TYPE(hash) == &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;
} Hash#store — псевдоним для Hash#[]=.
Связывает данное значение с данным ключом; возвращает возвращаемое значение.
Если данный ключ существует, заменяет его значение на данное значение; порядок не изменяется (см. Порядок элементов):
h = {foo: 0, bar: 1}
h[:foo] = 2 # => 2
h.store(:bar, 3) # => 3
h # => {:foo=>2, :bar=>3}
Если данный ключ не существует, добавляет пару ключ-значение; новая запись размещается в конце (см. Порядок элементов):
h = {foo: 0, bar: 1}
h[:baz] = 2 # => 2
h.store(:bat, 3) # => 3
h # => {:foo=>0, :bar=>1, :baz=>2, :bat=>3}
static VALUE
rb_hash_any_p(int argc, VALUE *argv, VALUE hash)
{
VALUE args[2];
args[0] = Qfalse;
rb_check_arity(argc, 0, 1);
if (RHASH_EMPTY_P(hash)) return Qfalse;
if (argc) {
if (rb_block_given_p()) {
rb_warn("given block not used");
}
args[1] = argv[0];
rb_hash_foreach(hash, any_p_i_pattern, (VALUE)args);
}
else {
if (!rb_block_given_p()) {
/* yields pairs, never false */
return Qtrue;
}
if (rb_block_pair_yield_optimizable())
rb_hash_foreach(hash, any_p_i_fast, (VALUE)args);
else
rb_hash_foreach(hash, any_p_i, (VALUE)args);
}
return args[0];
} Возвращает true, если хотя бы один элемент удовлетворяет заданному критерию; в противном случае — false.
Без аргумента и блока, возвращает true, если self не пуста; в противном случае — false.
С аргументом object и без блока, возвращает true, если для любого ключа key h.assoc(key) == object:
h = {foo: 0, bar: 1, baz: 2}
h.any?([:bar, 1]) # => true
h.any?([:bar, 0]) # => false
h.any?([:baz, 1]) # => false
Без аргумента и с блоком, вызывает блок для каждой пары ключ-значение; возвращает true, если блок возвращает какое-либо истинное значение, в противном случае — false:
h = {foo: 0, bar: 1, baz: 2}
h.any? {|key, value| value < 3 } # => true
h.any? {|key, value| value > 3 } # => false
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;
ar_force_convert_table(hash, __FILE__, __LINE__);
HASH_ASSERT(RHASH_ST_TABLE_P(hash));
table = RHASH_ST_TABLE(hash);
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];
} Если заданный ключ найден, возвращает массив из двух элементов, содержащий этот ключ и его значение:
h = {foo: 0, bar: 1, baz: 2}
h.assoc(:bar) # => [:bar, 1]
Возвращает nil, если ключ key не найден.
VALUE
rb_hash_clear(VALUE hash)
{
rb_hash_modify_check(hash);
if (RHASH_ITER_LEV(hash) > 0) {
rb_hash_foreach(hash, clear_i, 0);
}
else if (RHASH_AR_TABLE_P(hash)) {
ar_clear(hash);
}
else {
st_clear(RHASH_ST_TABLE(hash));
}
return hash;
} Удаляет все записи в хэше; возвращает self.
static VALUE
rb_hash_compact(VALUE hash)
{
VALUE result = rb_hash_new();
if (!RHASH_EMPTY_P(hash)) {
rb_hash_foreach(hash, set_if_not_nil, result);
}
return result;
} Возвращает копию self со всеми значениями nil удаленными:
h = {foo: 0, bar: nil, baz: 2, bat: nil}
h1 = h.compact
h1 # => {:foo=>0, :baz=>2}
static VALUE
rb_hash_compact_bang(VALUE hash)
{
st_index_t n;
rb_hash_modify_check(hash);
n = RHASH_SIZE(hash);
if (n) {
rb_hash_foreach(hash, delete_if_nil, hash);
if (n != RHASH_SIZE(hash))
return hash;
}
return Qnil;
} Возвращает self со всеми значениями nil удаленными (вместо создания копии):
h = {foo: 0, bar: nil, baz: 2, bat: nil}
h.compact! # => {:foo=>0, :baz=>2}
Возвращает nil, если ни одна запись не была удалена.
static VALUE
rb_hash_compare_by_id(VALUE hash)
{
VALUE tmp;
st_table *identtable;
if (rb_hash_compare_by_id_p(hash)) return hash;
rb_hash_modify_check(hash);
ar_force_convert_table(hash, __FILE__, __LINE__);
HASH_ASSERT(RHASH_ST_TABLE_P(hash));
tmp = hash_alloc(0);
identtable = rb_init_identtable_with_size(RHASH_SIZE(hash));
RHASH_ST_TABLE_SET(tmp, identtable);
rb_hash_foreach(hash, rb_hash_rehash_i, (VALUE)tmp);
st_free_table(RHASH_ST_TABLE(hash));
RHASH_ST_TABLE_SET(hash, identtable);
RHASH_ST_CLEAR(tmp);
rb_gc_force_recycle(tmp);
return hash;
} Устанавливает self учитывать только идентичность при сравнении ключей; два ключа считаются одинаковыми только если они являются одним и тем же объектом; возвращает self.
По умолчанию, эти два объекта рассматриваются как один и тот же ключ, поэтому s1 перезапишет s0:
s0 = 'x'
s1 = 'x'
h = {}
h.compare_by_identity? # => false
h[s0] = 0
h[s1] = 1
h # => {"x"=>1}
После вызова #compare_by_identity ключи считаются разными, и поэтому не перезаписывают друг друга:
h = {}
h.compare_by_identity # => {}
h.compare_by_identity? # => true
h[s0] = 0
h[s1] = 1
h # => {"x"=>0, "x"=>1}
MJIT_FUNC_EXPORTED VALUE
rb_hash_compare_by_id_p(VALUE hash)
{
if (RHASH_ST_TABLE_P(hash) && RHASH_ST_TABLE(hash)->type == &identhash) {
return Qtrue;
}
else {
return Qfalse;
}
} Возвращает true, если compare_by_identity был вызван, в противном случае — false.
static VALUE
rb_hash_deconstruct_keys(VALUE hash, VALUE keys)
{
return hash;
} static VALUE
rb_hash_default(int argc, VALUE *argv, VALUE hash)
{
VALUE ifnone;
rb_check_arity(argc, 0, 1);
ifnone = RHASH_IFNONE(hash);
if (FL_TEST(hash, RHASH_PROC_DEFAULT)) {
if (argc == 0) return Qnil;
return call_default_proc(ifnone, hash, argv[0]);
}
return ifnone;
} Возвращает значение по умолчанию для данного ключа. Возвращаемое значение определяется либо функцией по умолчанию, либо значением по умолчанию. См. Значения по умолчанию.
Без аргумента, возвращает текущее значение по умолчанию:
h = {}
h.default # => nil
Если key задан, возвращает значение по умолчанию для key, независимо от того, существует ли этот ключ:
h = Hash.new { |hash, key| hash[key] = "No key #{key}"}
h[:foo] = "Hello"
h.default(:foo) # => "No key foo"
static VALUE
rb_hash_set_default(VALUE hash, VALUE ifnone)
{
rb_hash_modify_check(hash);
SET_DEFAULT(hash, ifnone);
return ifnone;
} Устанавливает значение по умолчанию в value; возвращает value:
h = {}
h.default # => nil
h.default = false # => false
h.default # => false
static VALUE
rb_hash_default_proc(VALUE hash)
{
if (FL_TEST(hash, RHASH_PROC_DEFAULT)) {
return RHASH_IFNONE(hash);
}
return Qnil;
} Возвращает функцию по умолчанию для хэша (см. Значения по умолчанию):
h = {}
h.default_proc # => nil
h.default_proc = proc {|hash, key| "Default value for #{key}" }
h.default_proc.class # => Proc
VALUE
rb_hash_set_default_proc(VALUE hash, VALUE proc)
{
VALUE b;
rb_hash_modify_check(hash);
if (NIL_P(proc)) {
SET_DEFAULT(hash, proc);
return proc;
}
b = rb_check_convert_type_with_id(proc, T_DATA, "Proc", idTo_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;
SET_PROC_DEFAULT(hash, proc);
return proc;
} Устанавливает значение по умолчанию для self в proc: (см. Значения по умолчанию):
h = {}
h.default_proc # => nil
h.default_proc = proc { |hash, key| "Default value for #{key}" }
h.default_proc.class # => Proc
h.default_proc = nil
h.default_proc # => nil
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;
}
}
} Удаляет запись для заданного key и возвращает связанное с ним значение.
Если блок не задан и key найден, удаляет запись и возвращает связанное значение:
h = {foo: 0, bar: 1, baz: 2}
h.delete(:bar) # => 1
h # => {:foo=>0, :baz=>2}
Если блок не задан и key не найден, возвращает nil.
Если задан блок и key найден, игнорирует блок, удаляет запись и возвращает связанное значение:
h = {foo: 0, bar: 1, baz: 2}
h.delete(:baz) { |key| raise 'Will never happen'} # => 2
h # => {:foo=>0, :bar=>1}
Если задан блок и key не найден, вызывает блок и возвращает результат его выполнения:
h = {foo: 0, bar: 1, baz: 2}
h.delete(:nosuch) { |key| "Key #{key} not found" } # => "Key nosuch not found"
h # => {:foo=>0, :bar=>1, :baz=>2}
VALUE
rb_hash_delete_if(VALUE hash)
{
RETURN_SIZED_ENUMERATOR(hash, 0, 0, hash_enum_size);
rb_hash_modify_check(hash);
if (!RHASH_TABLE_EMPTY_P(hash)) {
rb_hash_foreach(hash, delete_if_i, hash);
}
return hash;
} Если задан блок, вызывает его для каждой пары ключ-значение; удаляет каждую запись, для которой блок возвращает истинное значение; возвращает self:
h = {foo: 0, bar: 1, baz: 2}
h.delete_if {|key, value| value > 0 } # => {:foo=>0}
Если блок не задан, возвращает новый Enumerator:
h = {foo: 0, bar: 1, baz: 2}
e = h.delete_if # => #<Enumerator: {:foo=>0, :bar=>1, :baz=>2}:delete_if>
e.each { |key, value| value > 0 } # => {:foo=>0}
static VALUE
rb_hash_dig(int argc, VALUE *argv, VALUE self)
{
rb_check_arity(argc, 1, UNLIMITED_ARGUMENTS);
self = rb_hash_aref(self, *argv);
if (!--argc) return self;
++argv;
return rb_obj_dig(argc, argv, self, Qnil);
} Находит и возвращает объект в вложенных объектах, указанный key и identifiers. Вложенные объекты могут быть экземплярами различных классов. См. Методы Dig.
Вложенные Hashes:
h = {foo: {bar: {baz: 2}}}
h.dig(:foo) # => {:bar=>{:baz=>2}}
h.dig(:foo, :bar) # => {:bar=>{:baz=>2}}
h.dig(:foo, :bar, :baz) # => 2
h.dig(:foo, :bar, :BAZ) # => nil
Вложенные Hashes и массивы:
h = {foo: {bar: [:a, :b, :c]}}
h.dig(:foo, :bar, 2) # => :c
Этот метод будет использовать значения по умолчанию для ключей, которые отсутствуют:
h = {foo: {bar: [:a, :b, :c]}}
h.dig(:hello) # => nil
h.default_proc = -> (hash, _key) { hash }
h.dig(:hello, :world) # => h
h.dig(:hello, :world, :foo, :bar, 2) # => :c
Hash#each — псевдоним для Hash#each_pair.
Вызывает заданный блок с каждой парой ключ-значение; возвращает self:
h = {foo: 0, bar: 1, baz: 2}
h.each_pair {|key, value| puts "#{key}: #{value}"} # => {:foo=>0, :bar=>1, :baz=>2}
Вывод:
foo: 0 bar: 1 baz: 2
Возвращает новый Enumerator, если блок не задан:
h = {foo: 0, bar: 1, baz: 2}
e = h.each_pair # => #<Enumerator: {:foo=>0, :bar=>1, :baz=>2}:each_pair>
h1 = e.each {|key, value| puts "#{key}: #{value}"}
h1 # => {:foo=>0, :bar=>1, :baz=>2}
Вывод:
foo: 0 bar: 1 baz: 2
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;
} Вызывает заданный блок с каждым ключом; возвращает self:
h = {foo: 0, bar: 1, baz: 2}
h.each_key {|key| puts key } # => {:foo=>0, :bar=>1, :baz=>2}
Вывод:
foo bar baz
Возвращает новый Enumerator, если блок не задан:
h = {foo: 0, bar: 1, baz: 2}
e = h.each_key # => #<Enumerator: {:foo=>0, :bar=>1, :baz=>2}:each_key>
h1 = e.each {|key| puts key }
h1 # => {:foo=>0, :bar=>1, :baz=>2}
Вывод:
foo bar baz
static VALUE
rb_hash_each_pair(VALUE hash)
{
RETURN_SIZED_ENUMERATOR(hash, 0, 0, hash_enum_size);
if (rb_block_pair_yield_optimizable())
rb_hash_foreach(hash, each_pair_i_fast, 0);
else
rb_hash_foreach(hash, each_pair_i, 0);
return hash;
} Hash#each — псевдоним для Hash#each_pair.
Вызывает заданный блок с каждой парой ключ-значение; возвращает self:
h = {foo: 0, bar: 1, baz: 2}
h.each_pair {|key, value| puts "#{key}: #{value}"} # => {:foo=>0, :bar=>1, :baz=>2}
Вывод:
foo: 0 bar: 1 baz: 2
Возвращает новый Enumerator, если блок не задан:
h = {foo: 0, bar: 1, baz: 2}
e = h.each_pair # => #<Enumerator: {:foo=>0, :bar=>1, :baz=>2}:each_pair>
h1 = e.each {|key, value| puts "#{key}: #{value}"}
h1 # => {:foo=>0, :bar=>1, :baz=>2}
Вывод:
foo: 0 bar: 1 baz: 2
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;
} Вызывает заданный блок с каждым значением; возвращает self:
h = {foo: 0, bar: 1, baz: 2}
h.each_value {|value| puts value } # => {:foo=>0, :bar=>1, :baz=>2}
Вывод:
0 1 2
Возвращает новый Enumerator, если блок не задан:
h = {foo: 0, bar: 1, baz: 2}
e = h.each_value # => #<Enumerator: {:foo=>0, :bar=>1, :baz=>2}:each_value>
h1 = e.each {|value| puts value }
h1 # => {:foo=>0, :bar=>1, :baz=>2}
Вывод:
0 1 2
static VALUE
rb_hash_empty_p(VALUE hash)
{
return RHASH_EMPTY_P(hash) ? Qtrue : Qfalse;
} Возвращает true если нет записей в хеше, false в противном случае:
{}.empty? # => true
{foo: 0, bar: 1, baz: 2}.empty? # => false
static VALUE
rb_hash_eql(VALUE hash1, VALUE hash2)
{
return hash_equal(hash1, hash2, TRUE);
} Возвращает true если все следующие условия истинны:
-
object— объект Hash. -
hashиobjectимеют одинаковые ключи (независимо от порядка). -
Для каждого ключа
key,h[key] eql? object[key].
В противном случае возвращает false.
Равно:
h1 = {foo: 0, bar: 1, baz: 2}
h2 = {foo: 0, bar: 1, baz: 2}
h1.eql? h2 # => true
h3 = {baz: 2, bar: 1, foo: 0}
h1.eql? h3 # => true
static VALUE
rb_hash_except(int argc, VALUE *argv, VALUE hash)
{
int i;
VALUE key, result;
result = hash_alloc(rb_cHash);
hash_copy(result, hash);
for (i = 0; i < argc; i++) {
key = argv[i];
rb_hash_delete(result, key);
}
return result;
} Возвращает новый Hash, исключая записи для заданных keys:
h = { a: 100, b: 200, c: 300 }
h.except(:a) #=> {:b=>200, :c=>300}
Любые заданные keys ключи, которые не найдены, игнорируются.
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 (hash_stlike_lookup(hash, key, &val)) {
return (VALUE)val;
}
else {
if (block_given) {
return rb_yield(key);
}
else if (argc == 1) {
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_key_err_raise(rb_sprintf("key not found: %"PRIsVALUE, desc), hash, key);
}
else {
return argv[1];
}
}
} Возвращает значение для заданного key, если оно найдено.
h = {foo: 0, bar: 1, baz: 2}
h.fetch(:bar) # => 1
Если key не найден и блок не задан, возвращает default_value:
{}.fetch(:nosuch, :default) # => :default
{}.fetch(:nosuch) # => nil
Если key не найден и задан блок, передает key блоку и возвращает результат выполнения блока:
{}.fetch(:nosuch) {|key| "No key #{key}"} # => "No key nosuch"
Вызывает исключение KeyError, если ни default_value ни блок не заданы.
Обратите внимание, что этот метод не использует значения default или default_proc.
static VALUE
rb_hash_fetch_values(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_fetch(hash, argv[i]));
}
return result;
} Возвращает новый массив, содержащий значения, связанные с заданными ключами *keys:
h = {foo: 0, bar: 1, baz: 2}
h.fetch_values(:baz, :foo) # => [2, 0]
Возвращает новый пустой массив, если аргументы не заданы.
При заданном блоке, вызывает блок для каждого отсутствующего ключа, рассматривая результат выполнения блока как значение для этого ключа:
h = {foo: 0, bar: 1, baz: 2}
values = h.fetch_values(:bar, :foo, :bad, :bam) {|key| key.to_s}
values # => [1, 0, "bad", "bam"]
Если блок не задан, выводит исключение, если любой заданный ключ не найден.
Hash#filter — псевдоним для Hash#select.
Возвращает новый объект Hash, записи которого соответствуют тем, для которых блок возвращает истинное значение:
h = {foo: 0, bar: 1, baz: 2}
h.select {|key, value| value < 2 } # => {:foo=>0, :bar=>1}
Возвращает новый Enumerator, если блок не задан:
h = {foo: 0, bar: 1, baz: 2}
e = h.select # => #<Enumerator: {:foo=>0, :bar=>1, :baz=>2}:select>
e.each {|key, value| value < 2 } # => {:foo=>0, :bar=>1}
Hash#filter! — псевдоним для Hash#select!.
Возвращает self, записи которого соответствуют тем, для которых блок возвращает истинное значение:
h = {foo: 0, bar: 1, baz: 2}
h.select! {|key, value| value < 2 } => {:foo=>0, :bar=>1} Возвращает nil если записи не были удалены.
Возвращает новый Enumerator, если блок не задан:
h = {foo: 0, bar: 1, baz: 2}
e = h.select! # => #<Enumerator: {:foo=>0, :bar=>1, :baz=>2}:select!>
e.each { |key, value| value < 2 } # => {:foo=>0, :bar=>1}
static VALUE
rb_hash_flatten(int argc, VALUE *argv, VALUE hash)
{
VALUE ary;
rb_check_arity(argc, 0, 1);
if (argc) {
int level = NUM2INT(argv[0]);
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);
level--;
if (level > 0) {
VALUE ary_flatten_level = INT2FIX(level);
rb_funcallv(ary, id_flatten_bang, 1, &ary_flatten_level);
}
else if (level < 0) {
/* flatten recursively */
rb_funcallv(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, представляющий одномерное разворачивание self.
По умолчанию вложенные массивы не раскладываются:
h = {foo: 0, bar: [:bat, 3], baz: 2}
h.flatten # => [:foo, 0, :bar, [:bat, 3], :baz, 2]
Принимает глубину рекурсивного разворачивания из целого аргумента level:
h = {foo: 0, bar: [:bat, [:baz, [:bat, ]]]}
h.flatten(1) # => [:foo, 0, :bar, [:bat, [:baz, [:bat]]]]
h.flatten(2) # => [:foo, 0, :bar, :bat, [:baz, [:bat]]]
h.flatten(3) # => [:foo, 0, :bar, :bat, :baz, [:bat]]
h.flatten(4) # => [:foo, 0, :bar, :bat, :baz, :bat]
Когда level отрицательное, разворачивает все вложенные массивы:
h = {foo: 0, bar: [:bat, [:baz, [:bat, ]]]}
h.flatten(-1) # => [:foo, 0, :bar, :bat, :baz, :bat]
h.flatten(-2) # => [:foo, 0, :bar, :bat, :baz, :bat]
Когда level равно нулю, возвращает эквивалент to_a :
h = {foo: 0, bar: [:bat, 3], baz: 2}
h.flatten(0) # => [[:foo, 0], [:bar, [:bat, 3]], [:baz, 2]]
h.flatten(0) == h.to_a # => true
Методы has_key?, key? и member? являются псевдонимами для #include?.
Возвращает true, если key является ключом в self, в противном случае 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, если value является значением в self, в противном случае 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 ST2FIX(hval);
} Возвращает целочисленный код хэша для хэша.
Два объекта Hash имеют одинаковый код хэша, если их содержимое одинаково (независимо от порядка):
h1 = {foo: 0, bar: 1, baz: 2}
h2 = {baz: 2, bar: 1, foo: 0}
h2.hash == h1.hash # => true
h2.eql? h1 # => true
static VALUE
rb_hash_replace(VALUE hash, VALUE hash2)
{
rb_hash_modify_check(hash);
if (hash == hash2) return hash;
if (RHASH_ITER_LEV(hash) > 0) {
rb_raise(rb_eRuntimeError, "can't replace hash during iteration");
}
hash2 = to_hash(hash2);
COPY_DEFAULT(hash, hash2);
if (RHASH_AR_TABLE_P(hash)) {
if (RHASH_AR_TABLE_P(hash2)) {
ar_clear(hash);
}
else {
ar_free_and_clear_table(hash);
RHASH_ST_TABLE_SET(hash, st_init_table_with_size(RHASH_TYPE(hash2), RHASH_SIZE(hash2)));
}
}
else {
if (RHASH_AR_TABLE_P(hash2)) {
st_free_table(RHASH_ST_TABLE(hash));
RHASH_ST_CLEAR(hash);
}
else {
st_clear(RHASH_ST_TABLE(hash));
RHASH_TBL_RAW(hash)->type = RHASH_ST_TABLE(hash2)->type;
}
}
rb_hash_foreach(hash2, rb_hash_rehash_i, (VALUE)hash);
rb_gc_writebarrier_remember(hash);
return hash;
} Заменяет все содержимое self содержимым other_hash; возвращает self:
h = {foo: 0, bar: 1, baz: 2}
h.replace({bat: 3, bam: 4}) # => {:bat=>3, :bam=>4}
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 = {foo: 0, bar: 1, baz: 2}
h.inspect # => "{:foo=>0, :bar=>1, :baz=>2}"
Hash#to_s является псевдонимом для Hash#inspect.
static VALUE
rb_hash_invert(VALUE hash)
{
VALUE h = rb_hash_new_with_size(RHASH_SIZE(hash));
rb_hash_foreach(hash, rb_hash_invert_i, h);
return h;
} Возвращает новый объект Hash с инвертированными парами ключ-значение:
h = {foo: 0, bar: 1, baz: 2}
h1 = h.invert
h1 # => {0=>:foo, 1=>:bar, 2=>:baz}
Перезаписывает любые повторяющиеся новые ключи: (см. Порядок записей):
h = {foo: 0, bar: 0, baz: 0}
h.invert # => {0=>:baz}
static VALUE
rb_hash_keep_if(VALUE hash)
{
RETURN_SIZED_ENUMERATOR(hash, 0, 0, hash_enum_size);
rb_hash_modify_check(hash);
if (!RHASH_TABLE_EMPTY_P(hash)) {
rb_hash_foreach(hash, keep_if_i, hash);
}
return hash;
} Вызывает блок для каждой пары ключ-значение; сохраняет запись, если блок возвращает истинное значение; в противном случае удаляет запись; возвращает self.
h = {foo: 0, bar: 1, baz: 2}
h.keep_if { |key, value| key.start_with?('b') } # => {:bar=>1, :baz=>2}
Возвращает новый перечислитель, если блок не задан:
h = {foo: 0, bar: 1, baz: 2}
e = h.keep_if # => #<Enumerator: {:foo=>0, :bar=>1, :baz=>2}:keep_if>
e.each { |key, value| key.start_with?('b') } # => {:bar=>1, :baz=>2}
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];
} Возвращает ключ для первой найденной записи со заданным value (см. Порядок записей):
h = {foo: 0, bar: 2, baz: 2}
h.key(0) # => :foo
h.key(2) # => :bar
Возвращает nil, если такой ключ не найден.
Методы has_key?, key? и member? являются псевдонимами для #include?.
Возвращает true, если key является ключом в self, в противном случае false.
MJIT_FUNC_EXPORTED VALUE
rb_hash_keys(VALUE hash)
{
st_index_t size = RHASH_SIZE(hash);
VALUE keys = rb_ary_new_capa(size);
if (size == 0) return keys;
if (ST_DATA_COMPATIBLE_P(VALUE)) {
RARRAY_PTR_USE_TRANSIENT(keys, ptr, {
if (RHASH_AR_TABLE_P(hash)) {
size = ar_keys(hash, ptr, size);
}
else {
st_table *table = RHASH_ST_TABLE(hash);
size = st_keys(table, ptr, size);
}
});
rb_gc_writebarrier_remember(keys);
rb_ary_set_len(keys, size);
}
else {
rb_hash_foreach(hash, keys_i, keys);
}
return keys;
} Возвращает новый массив, содержащий все ключи в self:
h = {foo: 0, bar: 1, baz: 2}
h.keys # => [:foo, :bar, :baz]
Возвращает количество записей в self:
{foo: 0, bar: 1, baz: 2}.length # => 3
Hash#length является псевдонимом для Hash#size.
Методы has_key?, key? и member? являются псевдонимами для #include?.
Возвращает true, если key является ключом в self, в противном случае false.
static VALUE
rb_hash_merge(int argc, VALUE *argv, VALUE self)
{
return rb_hash_update(argc, argv, rb_hash_dup(self));
} Возвращает новый Hash, сформированный путём слияния каждого из other_hashes в копию self.
Каждый аргумент в other_hashes должен быть Hash.
С аргументами и без блока:
-
Возвращает новый объект Hash, сформированный путём слияния каждого последующего Hash в
other_hashesвself. -
Каждая новая запись ключа добавляется в конец.
-
Каждая запись ключа-дубликата перезаписывает предыдущее значение.
Пример:
h = {foo: 0, bar: 1, baz: 2}
h1 = {bat: 3, bar: 4}
h2 = {bam: 5, bat:6}
h.merge(h1, h2) # => {:foo=>0, :bar=>4, :baz=>2, :bat=>6, :bam=>5}
С аргументами и блоком:
-
Возвращает новый объект Hash, который является результатом слияния
selfи каждого данного хэша. -
Данные хэши сливаются слева направо.
-
Каждая новая запись ключа добавляется в конец.
-
Для каждого дублирующего ключа:
-
Вызывает блок с ключом и старыми и новыми значениями.
-
Возвращаемое значение блока становится новым значением для записи.
-
Пример:
h = {foo: 0, bar: 1, baz: 2}
h1 = {bat: 3, bar: 4}
h2 = {bam: 5, bat:6}
h3 = h.merge(h1, h2) { |key, old_value, new_value| old_value + new_value }
h3 # => {:foo=>0, :bar=>5, :baz=>2, :bat=>9, :bam=>5}
Без аргументов:
-
Возвращает копию
self. -
Блок, если задан, игнорируется.
Пример:
h = {foo: 0, bar: 1, baz: 2}
h.merge # => {:foo=>0, :bar=>1, :baz=>2}
h1 = h.merge { |key, old_value, new_value| raise 'Cannot happen' }
h1 # => {:foo=>0, :bar=>1, :baz=>2}
Объединяет каждый из other_hashes в self; возвращает self.
Каждый аргумент в other_hashes должен быть объектом типа Hash.
Метод update является псевдонимом для #merge!.
С аргументами и без блока:
-
Возвращает
self, после объединения переданных словарей в него. -
Переданные словари объединяются слева направо.
-
Каждая новая запись добавляется в конец.
-
Значение каждой записи с дублирующимся ключом перезаписывает предыдущее значение.
Пример:
h = {foo: 0, bar: 1, baz: 2}
h1 = {bat: 3, bar: 4}
h2 = {bam: 5, bat:6}
h.merge!(h1, h2) # => {:foo=>0, :bar=>4, :baz=>2, :bat=>6, :bam=>5}
С аргументами и блоком:
-
Возвращает
self, после объединения переданных словарей. -
Переданные словари объединяются слева направо.
-
Каждая новая запись добавляется в конец.
-
Для каждого дублирующегося ключа:
-
Вызывает блок с ключом и старым и новым значениями.
-
Возвращаемое значение блока становится новым значением для записи.
-
Пример:
h = {foo: 0, bar: 1, baz: 2}
h1 = {bat: 3, bar: 4}
h2 = {bam: 5, bat:6}
h3 = h.merge!(h1, h2) { |key, old_value, new_value| old_value + new_value }
h3 # => {:foo=>0, :bar=>5, :baz=>2, :bat=>9, :bam=>5}
Без аргументов:
-
Возвращает
self, без изменений. -
Блок, если задан, игнорируется.
Пример:
h = {foo: 0, bar: 1, baz: 2}
h.merge # => {:foo=>0, :bar=>1, :baz=>2}
h1 = h.merge! { |key, old_value, new_value| raise 'Cannot happen' }
h1 # => {:foo=>0, :bar=>1, :baz=>2}
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];
} Возвращает новый массив из 2 элементов, содержащий ключ и значение первой найденной записи, значение которой соответствует == значению (см. Порядок записей):
h = {foo: 0, bar: 1, baz: 1}
h.rassoc(1) # => [:bar, 1]
Возвращает nil если такое значение не найдено.
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_AR_TABLE_P(hash)) {
tmp = hash_alloc(0);
ar_alloc_table(tmp);
rb_hash_foreach(hash, rb_hash_rehash_i, (VALUE)tmp);
ar_free_and_clear_table(hash);
ar_copy(hash, tmp);
ar_free_and_clear_table(tmp);
}
else if (RHASH_ST_TABLE_P(hash)) {
st_table *old_tab = RHASH_ST_TABLE(hash);
tmp = hash_alloc(0);
tbl = st_init_table_with_size(old_tab->type, old_tab->num_entries);
RHASH_ST_TABLE_SET(tmp, tbl);
rb_hash_foreach(hash, rb_hash_rehash_i, (VALUE)tmp);
st_free_table(old_tab);
RHASH_ST_TABLE_SET(hash, tbl);
RHASH_ST_CLEAR(tmp);
}
hash_verify(hash);
return hash;
} Перестраивает таблицу хешей, пересчитывая хеш-индекс для каждого ключа; возвращает self.
Таблица хешей становится недействительной, если значение хеша ключа изменилось после создания записи. См. Изменение активного ключа хеша.
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;
} Возвращает новый объект Hash, записи которого — все записи из self, для которых блок возвращает false или nil:
h = {foo: 0, bar: 1, baz: 2}
h1 = h.reject {|key, value| key.start_with?('b') }
h1 # => {:foo=>0}
Возвращает новый перечислитель, если блок не задан:
h = {foo: 0, bar: 1, baz: 2}
e = h.reject # => #<Enumerator: {:foo=>0, :bar=>1, :baz=>2}:reject>
h1 = e.each {|key, value| key.start_with?('b') }
h1 # => {:foo=>0}
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_SIZE(hash)) return Qnil;
return hash;
} Возвращает self, оставшиеся записи которого — те, для которых блок возвращает false или nil:
h = {foo: 0, bar: 1, baz: 2}
h.reject! {|key, value| value < 2 } # => {:baz=>2}
Возвращает nil если записи не были удалены.
Возвращает новый перечислитель, если блок не задан:
h = {foo: 0, bar: 1, baz: 2}
e = h.reject! # => #<Enumerator: {:foo=>0, :bar=>1, :baz=>2}:reject!>
e.each {|key, value| key.start_with?('b') } # => {:foo=>0}
Заменяет все содержимое self содержимым other_hash; возвращает self:
h = {foo: 0, bar: 1, baz: 2}
h.replace({bat: 3, bam: 4}) # => {:bat=>3, :bam=>4}
static 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;
} Hash#filter является псевдонимом для Hash#select.
Возвращает новый объект Hash, записи которого — записи, для которых блок возвращает истинное значение:
h = {foo: 0, bar: 1, baz: 2}
h.select {|key, value| value < 2 } # => {:foo=>0, :bar=>1}
Возвращает новый перечислитель, если блок не задан:
h = {foo: 0, bar: 1, baz: 2}
e = h.select # => #<Enumerator: {:foo=>0, :bar=>1, :baz=>2}:select>
e.each {|key, value| value < 2 } # => {:foo=>0, :bar=>1}
static 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);
n = RHASH_SIZE(hash);
if (!n) return Qnil;
rb_hash_foreach(hash, keep_if_i, hash);
if (n == RHASH_SIZE(hash)) return Qnil;
return hash;
} Hash#filter! является псевдонимом для Hash#select!.
Возвращает self, записи которого — записи, для которых блок возвращает истинное значение:
h = {foo: 0, bar: 1, baz: 2}
h.select! {|key, value| value < 2 } => {:foo=>0, :bar=>1} Возвращает nil если записи не были удалены.
Возвращает новый перечислитель, если блок не задан:
h = {foo: 0, bar: 1, baz: 2}
e = h.select! # => #<Enumerator: {:foo=>0, :bar=>1, :baz=>2}:select!>
e.each { |key, value| value < 2 } # => {:foo=>0, :bar=>1}
static VALUE
rb_hash_shift(VALUE hash)
{
struct shift_var var;
rb_hash_modify_check(hash);
if (RHASH_AR_TABLE_P(hash)) {
var.key = Qundef;
if (RHASH_ITER_LEV(hash) == 0) {
if (ar_shift(hash, &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);
}
}
}
if (RHASH_ST_TABLE_P(hash)) {
var.key = Qundef;
if (RHASH_ITER_LEV(hash) == 0) {
if (st_shift(RHASH_ST_TABLE(hash), &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 rb_hash_default_value(hash, Qnil);
} Удаляет первую запись словаря (см. Порядок записей); возвращает массив из 2 элементов, содержащий удаленный ключ и значение:
h = {foo: 0, bar: 1, baz: 2}
h.shift # => [:foo, 0]
h # => {:bar=>1, :baz=>2}
Возвращает значение по умолчанию, если словарь пуст (см. Значения по умолчанию).
VALUE
rb_hash_size(VALUE hash)
{
return INT2FIX(RHASH_SIZE(hash));
} Возвращает количество записей в self:
{foo: 0, bar: 1, baz: 2}.length # => 3
Hash#length является псевдонимом для Hash#size.
static VALUE
rb_hash_slice(int argc, VALUE *argv, VALUE hash)
{
int i;
VALUE key, value, result;
if (argc == 0 || RHASH_EMPTY_P(hash)) {
return rb_hash_new();
}
result = rb_hash_new_with_size(argc);
for (i = 0; i < argc; i++) {
key = argv[i];
value = rb_hash_lookup2(hash, key, Qundef);
if (value != Qundef)
rb_hash_aset(result, key, value);
}
return result;
} Возвращает новый объект Hash, содержащий записи для заданных keys:
h = {foo: 0, bar: 1, baz: 2}
h.slice(:baz, :foo) # => {:baz=>2, :foo=>0}
Любые заданные keys которые не найдены, игнорируются.
Hash#store является псевдонимом для Hash#[]=.
Связывает заданный value с заданным key; возвращает value.
Если заданный key существует, заменяет его значение на заданное value; порядок не изменяется (см. Порядок записей):
h = {foo: 0, bar: 1}
h[:foo] = 2 # => 2
h.store(:bar, 3) # => 3
h # => {:foo=>2, :bar=>3}
Если key не существует, добавляет key и value; новая запись располагается в конце списка (см. Порядок записей):
h = {foo: 0, bar: 1}
h[:baz] = 2 # => 2
h.store(:bat, 3) # => 3
h # => {:foo=>0, :bar=>1, :baz=>2, :bat=>3}
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);
return ary;
} Возвращает новый массив объектов массивов из 2 элементов; каждый вложенный массив содержит пару ключ-значение из self:
h = {foo: 0, bar: 1, baz: 2}
h.to_a # => [[:foo, 0], [:bar, 1], [:baz, 2]]
static VALUE
rb_hash_to_h(VALUE hash)
{
if (rb_block_given_p()) {
return rb_hash_to_h_block(hash);
}
if (rb_obj_class(hash) != rb_cHash) {
const VALUE flags = RBASIC(hash)->flags;
hash = hash_dup(hash, rb_cHash, flags & RHASH_PROC_DEFAULT);
}
return hash;
} Для объекта Hash возвращает self.
Для подкласса Hash возвращает новый Hash, содержащий содержимое self.
При задании блока возвращает новый объект Hash, содержимое которого основано на блоке; блок должен возвращать объект массива из 2 элементов, определяющий пару ключ-значение, которая должна быть включена в возвращаемый массив:
h = {foo: 0, bar: 1, baz: 2}
h1 = h.to_h {|key, value| [value, key] }
h1 # => {0=>:foo, 1=>:bar, 2=>:baz}
static VALUE
rb_hash_to_hash(VALUE hash)
{
return hash;
} Возвращает self.
static VALUE
rb_hash_to_proc(VALUE hash)
{
return rb_func_lambda_new(hash_proc_call, hash, 1, 1);
} Возвращает объект Proc, который сопоставляет ключ значению:
h = {foo: 0, bar: 1, baz: 2}
proc = h.to_proc
proc.class # => Proc
proc.call(:foo) # => 0
proc.call(:bar) # => 1
proc.call(:nosuch) # => nil
Возвращает новую строку, содержащую записи хеша:
h = {foo: 0, bar: 1, baz: 2}
h.inspect # => "{:foo=>0, :bar=>1, :baz=>2}"
Hash#to_s является псевдонимом для Hash#inspect.
static VALUE
rb_hash_transform_keys(int argc, VALUE *argv, VALUE hash)
{
VALUE result;
struct transform_keys_args transarg = {0};
argc = rb_check_arity(argc, 0, 1);
if (argc > 0) {
transarg.trans = to_hash(argv[0]);
transarg.block_given = rb_block_given_p();
}
else {
RETURN_SIZED_ENUMERATOR(hash, 0, 0, hash_enum_size);
}
result = rb_hash_new();
if (!RHASH_EMPTY_P(hash)) {
if (transarg.trans) {
transarg.result = result;
rb_hash_foreach(hash, transform_keys_hash_i, (VALUE)&transarg);
}
else {
rb_hash_foreach(hash, transform_keys_i, result);
}
}
return result;
} Возвращает новый объект Hash; каждая запись имеет:
-
Ключ, предоставленный блоком.
-
Значение из
self.
Можно указать необязательный аргумент хеша для сопоставления ключей с новыми ключами. Любой ключ, не указанный, будет сопоставлен с помощью предоставленного блока или останется прежним, если блок не указан.
Преобразование ключей:
h = {foo: 0, bar: 1, baz: 2}
h1 = h.transform_keys {|key| key.to_s }
h1 # => {"foo"=>0, "bar"=>1, "baz"=>2}
h.transform_keys(foo: :bar, bar: :foo)
#=> {bar: 0, foo: 1, baz: 2}
h.transform_keys(foo: :hello, &:to_s)
#=> {:hello=>0, "bar"=>1, "baz"=>2}
Перезаписывает значения для дублирующих ключей:
h = {foo: 0, bar: 1, baz: 2}
h1 = h.transform_keys {|key| :bat }
h1 # => {:bat=>2}
Возвращает новый Enumerator, если блок не указан:
h = {foo: 0, bar: 1, baz: 2}
e = h.transform_keys # => #<Enumerator: {:foo=>0, :bar=>1, :baz=>2}:transform_keys>
h1 = e.each { |key| key.to_s }
h1 # => {"foo"=>0, "bar"=>1, "baz"=>2}
static VALUE
rb_hash_transform_keys_bang(int argc, VALUE *argv, VALUE hash)
{
VALUE trans = 0;
int block_given = 0;
argc = rb_check_arity(argc, 0, 1);
if (argc > 0) {
trans = to_hash(argv[0]);
block_given = rb_block_given_p();
}
else {
RETURN_SIZED_ENUMERATOR(hash, 0, 0, hash_enum_size);
}
rb_hash_modify_check(hash);
if (!RHASH_TABLE_EMPTY_P(hash)) {
long i;
VALUE pairs = rb_hash_flatten(0, NULL, hash);
rb_hash_clear(hash);
for (i = 0; i < RARRAY_LEN(pairs); i += 2) {
VALUE key = RARRAY_AREF(pairs, i), new_key, val;
if (!trans) {
new_key = rb_yield(key);
}
else if ((new_key = rb_hash_lookup2(trans, key, Qundef)) != Qundef) {
/* use the transformed key */
}
else if (block_given) {
new_key = rb_yield(key);
}
else {
new_key = key;
}
val = RARRAY_AREF(pairs, i+1);
rb_hash_aset(hash, new_key, val);
}
}
return hash;
} То же, что и Hash#transform_keys, но изменяет получатель на месте, а не возвращает новый хеш.
static VALUE
rb_hash_transform_values(VALUE hash)
{
VALUE result;
RETURN_SIZED_ENUMERATOR(hash, 0, 0, hash_enum_size);
result = hash_copy(hash_alloc(rb_cHash), hash);
SET_DEFAULT(result, Qnil);
if (!RHASH_EMPTY_P(hash)) {
rb_hash_stlike_foreach_with_replace(result, transform_values_foreach_func, transform_values_foreach_replace, result);
}
return result;
} Возвращает новый объект Hash; каждая запись имеет:
-
Ключ из
self. -
Значение, предоставленное блоком.
Преобразование значений:
h = {foo: 0, bar: 1, baz: 2}
h1 = h.transform_values {|value| value * 100}
h1 # => {:foo=>0, :bar=>100, :baz=>200}
Возвращает новый Enumerator, если блок не указан:
h = {foo: 0, bar: 1, baz: 2}
e = h.transform_values # => #<Enumerator: {:foo=>0, :bar=>1, :baz=>2}:transform_values>
h1 = e.each { |value| value * 100}
h1 # => {:foo=>0, :bar=>100, :baz=>200}
static VALUE
rb_hash_transform_values_bang(VALUE hash)
{
RETURN_SIZED_ENUMERATOR(hash, 0, 0, hash_enum_size);
rb_hash_modify_check(hash);
if (!RHASH_TABLE_EMPTY_P(hash)) {
rb_hash_stlike_foreach_with_replace(hash, transform_values_foreach_func, transform_values_foreach_replace, hash);
}
return hash;
} Возвращает self, ключи которого не изменены, а значения определяются заданным блоком.
h = {foo: 0, bar: 1, baz: 2}
h.transform_values! {|value| value * 100} # => {:foo=>0, :bar=>100, :baz=>200}
Возвращает новый Enumerator, если блок не указан:
h = {foo: 0, bar: 1, baz: 2}
e = h.transform_values! # => #<Enumerator: {:foo=>0, :bar=>100, :baz=>200}:transform_values!>
h1 = e.each {|value| value * 100}
h1 # => {:foo=>0, :bar=>100, :baz=>200}
static VALUE
rb_hash_update(int argc, VALUE *argv, VALUE self)
{
int i;
bool block_given = rb_block_given_p();
rb_hash_modify(self);
for (i = 0; i < argc; i++){
VALUE hash = to_hash(argv[i]);
if (block_given) {
rb_hash_foreach(hash, rb_hash_update_block_i, self);
}
else {
rb_hash_foreach(hash, rb_hash_update_i, self);
}
}
return self;
} Объединяет каждый из other_hashes в self; возвращает self.
Каждый аргумент в other_hashes должен быть хешем.
Метод update является псевдонимом для #merge!.
С аргументами и без блока:
-
Возвращает
self, после объединения заданных хешей в него. -
Заданные хеши объединяются слева направо.
-
Каждая новая запись добавляется в конец.
-
Значение каждой записи с дублирующим ключом перезаписывает предыдущее значение.
Пример:
h = {foo: 0, bar: 1, baz: 2}
h1 = {bat: 3, bar: 4}
h2 = {bam: 5, bat:6}
h.merge!(h1, h2) # => {:foo=>0, :bar=>4, :baz=>2, :bat=>6, :bam=>5}
С аргументами и блоком:
-
Возвращает
self, после объединения заданных хешей. -
Заданные хеши объединяются слева направо.
-
Каждая запись с новым ключом добавляется в конец.
-
Для каждого дублирующего ключа:
-
Вызывает блок с ключом и старыми и новыми значениями.
-
Возвращаемое значение блока становится новым значением для записи.
-
Пример:
h = {foo: 0, bar: 1, baz: 2}
h1 = {bat: 3, bar: 4}
h2 = {bam: 5, bat:6}
h3 = h.merge!(h1, h2) { |key, old_value, new_value| old_value + new_value }
h3 # => {:foo=>0, :bar=>5, :baz=>2, :bat=>9, :bam=>5}
Без аргументов:
-
Возвращает
self, без изменений. -
Блок, если он указан, игнорируется.
Пример:
h = {foo: 0, bar: 1, baz: 2}
h.merge # => {:foo=>0, :bar=>1, :baz=>2}
h1 = h.merge! { |key, old_value, new_value| raise 'Cannot happen' }
h1 # => {:foo=>0, :bar=>1, :baz=>2}
Возвращает true если value является значением в self, в противном случае 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)) {
if (RHASH_AR_TABLE_P(hash)) {
rb_gc_writebarrier_remember(values);
RARRAY_PTR_USE_TRANSIENT(values, ptr, {
size = ar_values(hash, ptr, size);
});
}
else if (RHASH_ST_TABLE_P(hash)) {
st_table *table = RHASH_ST_TABLE(hash);
rb_gc_writebarrier_remember(values);
RARRAY_PTR_USE_TRANSIENT(values, ptr, {
size = st_values(table, ptr, size);
});
}
rb_ary_set_len(values, size);
}
else {
rb_hash_foreach(hash, values_i, values);
}
return values;
} Возвращает новый массив, содержащий все значения в self:
h = {foo: 0, bar: 1, baz: 2}
h.values # => [0, 1, 2]
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;
} Возвращает новый массив, содержащий значения для заданных keys:
h = {foo: 0, bar: 1, baz: 2}
h.values_at(:baz, :foo) # => [2, 0]
Значения по умолчанию возвращаются для любых ключей, которые не найдены:
h.values_at(:hello, :foo) # => [nil, 0]
Ruby Core © 1993–2020 Yukihiro Matsumoto
Licensed under the Ruby License.
Ruby Standard Library © contributors
Licensed under their own licenses.