Первая статья этой серии была посвящена Array и основам идиоматической итерации Ruby. Массив — довольно гибкий класс, но есть более подходящие решения для конкретных ситуаций. В этой статье рассматриваются некоторые другие типы коллекций, которые поставляются с Ruby.
Хэш
Иногда вам нужно сопоставить одно значение другому. Например, вы можете сопоставить идентификатор продукта с массивом, содержащим информацию об этом продукте. Если бы идентификаторы продуктов были целыми числами, вы могли бы сделать это с массивом, но рискуя потратить много места между идентификаторами. Хэши Ruby функционируют как ассоциативные массивы, где ключи не ограничены целыми числами. Они похожи на словари Python.
Творчество
Как и массивы, хэши имеют как синтаксис инициализации литерала, так и конструктора.
>> colors = {}
>> colors['red'] = 0xff0000
>> colors = Hash.new
>> colors['red'] = 0xff0000
Как и в случае с массивами, можно создать хэш с начальными значениями. Здесь мы видим оператора idiomatic => («hash rocket»).
>> colors = {
>> 'red' => 0xff0000,
>> 'blue' => 0x0000ff
>> }
=> {"red"=>16711680, "blue"=>255}
Если вы попытаетесь получить доступ к хеш-ключу без значения, он вернет ноль. Вы можете изменить это значение по умолчанию, передав аргумент конструктору.
>> h = Hash.new
>> h[:blah]
=> nil
>> h = Hash.new(0)
>> h[:blah]
=> 0
Обратите внимание, что доступ к несуществующему ключу не создает его.
>> h = {}
>> h.size
=> 0
>> h[:blah]
>> h.size
=> 0
делеция
Если вы хотите удалить пару ключей из хеша, вы можете использовать #delete
Может быть заманчиво просто установить значение ключа равным nil, как в таблицах Lua, но ключ все равно будет частью хэша и, следовательно, будет включен в итерацию.
>> colors['red'] = nil
>> colors.size
=> 2
>> colors.delete('red')
>> colors.size
=> 1
итерация
Хэши повторяются как массивы, за исключением того, что в блоки передаются два значения вместо одного.
>> hash = {"Juan" => 24, "Isidora" => 35}
>> hash.each { |name, age| puts "#{name}: #{age}" }
Juan: 24
Isidora: 35
Блочные переменные, такие как name
age
Они произвольны и могут быть чем угодно, хотя рекомендуется делать их описательными.
Хэшировать ракету или не хэшировать ракету
Распространено использование символов в качестве ключей Hash, потому что они описательные, как строки, но быстрые, как целые числа.
>> farm_counts = {
>> :cow => 8,
>> :chicken => 23,
>> :pig => 11,
>> }
=> {:cow=>8, :chicken=>23, :pig=>11}
Начиная с Ruby 1.9, хэши, ключи которых являются символами, могут быть созданы без хэш-ракеты (=>), больше похожей на JavaScript или Python.
>> farm_counts = {
>> cow: 8,
>> chicken: 23,
>> pig: 11
>> }
=> {:cow=>8, :chicken=>23, :pig=>11}
Оба стиля распространены, но следует помнить, что все другие типы ключей по-прежнему используют хэш-ракету, поэтому использование двоеточия вместо одной части кода может привести к появлению новичков.
Аргументы с ключевыми словами
Python предоставляет возможность вызывать функции с помощью аргументов ключевых слов. При использовании ключевых слов нет необходимости передавать аргументы в определенном порядке или передавать какие-либо конкретные аргументы вообще. Хотя технически Ruby не предоставляет аргументы ключевых слов, для их моделирования можно использовать хеш. Если хеш является последним аргументом в вызове метода, фигурные скобки могут быть опущены.
>> class Person
>> attr_accessor :first, :last, :weight, :height
>> def initialize(params = {})
>> @first = params[:first]
>> @last = params[:last]
>> @weight = params[:weight]
>> @height = params[:height]
>> end
>> end
>> p = Person.new(
>> height: 170cm,
>> weight: 72,
>> last: 'Doe',
>> first: 'John'
>> )
Обратите внимание, что params = {}
ArgumentError
Меньшие хэши с полями массива
У кого-то появилась блестящая идея сделать более легкий хэш из класса Array .
$ gem install arrayfields
>> require 'arrayfields'
>> h = ArrayFields.new
>> h[:lunes] = "Monday"
>> h[:martes] = "Tuesday"
>> h.fields
=> [:lunes, :martes]
>> h.values
=> ["Monday", "Tuesday"]
Я не очень знаком с гемом arrayfields или с тем, как он применяется в различных реализациях Ruby, но он очень популярен в Ruby Toolbox, и если вы собираетесь сериализовать много данных Hash, возможно, стоит проверить.
наборы
Если вам нужна коллекция, в которой порядок не имеет значения, а элементы гарантированно будут уникальными, то вам, вероятно, нужен набор.
В отличие от других типов коллекций, вы должны добавить оператор require, чтобы использовать класс Set.
>> require 'set'
Кроме того, в отличие от Array и Hash, Set не имеет какого-либо специального литерального синтаксиса. Тем не менее, вы можете передать массив в Set#new
>> s = Set.new([1,2,3])
=> #<Set: {1, 2, 3}>
В качестве альтернативы вы можете использовать Array#to_set
>> [1,2,3,3].to_set
=> #<Set: {1, 2, 3}>
Set использует оператор <<
#add
#push
>> s = Set.new
>> s << 1
>> s.add 2
Чтобы удалить элемент из набора, используйте метод #delete
>> s.delete(1)
=> #<Set: {2}>
Как с массивом, #include?
может быть использован для тестирования членства.
>> s.include? 1
=> false
>> s.include? 2
=> true
Одна из полезных функций Set — то, что он не будет добавлять элементы, которые он уже включает.
>> s = Set.new [1,2]
=> #<Set: {1, 2}>
>> s.add 2
=> #<Set: {1, 2}>
Ранее я указывал, что Array может выполнять логические операции. Естественно, Сет может сделать это также.
>> s1 = [1,2,3].to_set
>> s2 = [2,3,4].to_set
>> s1 & s2
=> #<Set: {2, 3}>
>> s1 | s2
=> #<Set: {1, 2, 3, 4}>
В отличие от Array, он также может выполнять эксклюзивные операции или операции с оператором ^
>> [1,2,3] ^ [2,3,4]
=> NoMethodError: undefined method `^' for [1, 2, 3]:Array
>> s1 ^ s2
=> #<Set: {4, 1}>
Изменяется
Ранее в части I я указывал на диапазоны. Класс Range является своего рода квази-коллекцией. Он может повторяться, как и другие коллекции, использующие Enumerable, но он не является контейнером для произвольных элементов.
>> r = Range.new('a', 'c')
=> 'a'..'c'
>> r.each { |i| puts i }
a
b
c
Ранее я показал, что диапазоны могут нарезать массивы или создавать индексы для их итерации.
>> letters = [:a,:b,:c,:d,:e]
>> letters[1..3]
=> [:b, :c, :d]
>> (1..3).map { |i| letters[i].upcase }
=> [:B, :C, 😀]
В дополнение к нарезке массивов, диапазоны могут упростить логику оператора регистра.
>> def theme(year)
>> case year
>> when 1970..1979 then "War Bad, Black People Not Bad"
>> when 1980..1992 then "Cocaine, Money, and The Future"
>> when 1993..2000 then "Gillian Anderson, Sitcoms in The FriendZone, and AOL"
>> when 2000..2013 then "RIP, Music"
>> end
>> end
>> theme(1987)
=> "Cocaine, Money, and The Future"
Существует также вопрос о стекопереработке о генерации случайных строк, которые получили ответы на некоторые вопросы, которые хорошо используют диапазоны.
>> (0...10).map{ ('a'..'z').to_a[rand(26)] }.join
=> "vphkjxysly"
Вывод
Это охватывает хэши, наборы и диапазоны. В следующем посте я расскажу о Enumerable, Enumerator и об интересных вещах, которые вы можете сделать с помощью таких инструментов.