Sök…


Introduktion

A Hash är en ordbokliknande samling av unika nycklar och deras värden. Även kallad associativa matriser, de liknar Arrays, men där en array använder heltal som sitt index, med en Hash kan du använda valfri objekttyp. Du hämtar eller skapar en ny post i en Hash genom att hänvisa till dess nyckel.

Syntax

  • {first_name: "Noel", second_name: "Edmonds"}

  • {: first_name => "Noel",: second_name => "Edmonds"}

  • {"First Name" => "Noel", "Second Name" => "Edmonds"}

  • {first_key => first_value, second_key => second_value}

Anmärkningar

Hashes i Ruby-kartnycklar till värden med en hashtabell.

Alla hashbara objekt kan användas som nycklar. Det är emellertid mycket vanligt att använda en Symbol eftersom den generellt sett är mer effektiv i flera Ruby-versioner på grund av den minskade objektallokeringen.

{ key1: "foo", key2: "baz"  }

Skapa en hash

En hash i Ruby är ett objekt som implementerar en hashtabell och kartlägger nycklar till värden. Ruby stöder en specifik bokstavlig syntax för att definiera hascher med {} :

my_hash = {}  # an empty hash
grades = { 'Mark' => 15, 'Jimmy' => 10, 'Jack' => 10 }

En hash kan också skapas med den new standardmetoden:

my_hash = Hash.new  # any empty hash
my_hash = {}        # any empty hash

Hashar kan ha värden av vilken typ som helst, inklusive komplexa typer som matriser, objekt och andra hash:

mapping = { 'Mark' => 15, 'Jimmy' => [3,4], 'Nika' => {'a' => 3, 'b' => 5} }
mapping['Mark']   # => 15
mapping['Jimmy']  # => [3, 4]
mapping['Nika']   # => {"a"=>3, "b"=>5}

Även nycklar kan vara av valfri typ, inklusive komplexa sådana:

mapping = { 'Mark' => 15, 5 => 10, [1, 2] => 9 }
mapping['Mark']  # => 15
mapping[[1, 2]]  # => 9

Symboler används ofta som hash-nycklar, och Ruby 1.9 introducerade en ny syntax specifikt för att förkorta denna process. Följande hashes är likvärdiga:

# Valid on all Ruby versions
grades = { :Mark => 15, :Jimmy => 10, :Jack => 10 }
# Valid in Ruby version 1.9+
grades = { Mark: 15, Jimmy: 10, Jack: 10 }

Följande hash (giltigt i alla Ruby-versioner) är annorlunda eftersom alla nycklar är strängar:

grades = { "Mark" => 15, "Jimmy" => 10, "Jack" => 10 }

Medan båda syntaxversionerna kan blandas, avskräcks följande.

mapping = { :length => 45, width: 10 }

Med Ruby 2.2+ finns det en alternativ syntax för att skapa en hash med symboltangenter (mest användbar om symbolen innehåller mellanslag):

grades = { "Jimmy Choo": 10, :"Jack Sparrow": 10 }
# => { :"Jimmy Choo" => 10, :"Jack Sparrow" => 10}

Få tillgång till värden

Enskilda värden för en hash läses och skrivs med metoderna [] och []= :

my_hash = { length: 4, width: 5 }

my_hash[:length] #=> => 4

my_hash[:height] = 9

my_hash #=> {:length => 4, :width => 5, :height => 9 }

Som standard kommer åtkomst till en nyckel som inte har lagts till i hash nil , vilket innebär att det alltid är säkert att försöka slå upp en nyckels värde:

my_hash = {}

my_hash[:age] # => nil

Hashes kan också innehålla nycklar i strängar. Om du försöker komma åt dem normalt kommer det bara att returnera en nil , istället får du åtkomst till dem med strängnycklarna:

my_hash = { "name" => "user" }

my_hash[:name]    # => nil
my_hash["name"]   # => user

För situationer där nycklar förväntas eller måste existera, har hashes en fetch som kommer att göra ett undantag när du kommer åt en nyckel som inte finns:

my_hash = {}

my_hash.fetch(:age) #=> KeyError: key not found: :age

fetch accepterar ett standardvärde som sitt andra argument, som returneras om nyckeln inte har ställts in tidigare:

my_hash =  {}
my_hash.fetch(:age, 45) #=> => 45

fetch kan också acceptera ett block som returneras om nyckeln inte har ställts in tidigare:

my_hash = {}
my_hash.fetch(:age) { 21 } #=> 21

my_hash.fetch(:age) do |k|
  puts "Could not find #{k}"
end

#=> Could not find age

Hashes stöder också en store som ett alias för []= :

my_hash = {}

my_hash.store(:age, 45)

my_hash #=> { :age => 45 }

Du kan också få alla värden på en hash med hjälp av values :

my_hash = { length: 4, width: 5 }

my_hash.values #=> [4, 5]

Obs! Detta är bara för Ruby 2.3+ #dig är praktiskt för kapslade Hash . Extraherar det kapslade värdet som specificeras av sekvensen för idx-objekt genom att ringa dig vid varje steg och returnera noll om något mellansteg är noll.

h = { foo: {bar: {baz: 1}}}

h.dig(:foo, :bar, :baz)   # => 1
h.dig(:foo, :zot, :xyz)   # => nil

g = { foo: [10, 11, 12] }
g.dig(:foo, 1)            # => 11

Ställa in standardvärden

Som standard försöker slå värdet för en nyckel som inte existerar kommer tillbaka nil . Du kan valfritt ange något annat värde som ska returneras (eller en åtgärd att vidta) när hash har åtkomst med en icke-existerande nyckel. Även om detta kallas "standardvärdet", behöver det inte vara ett enda värde; det kan till exempel vara ett beräknat värde som tangentens längd.

Standardvärdet för en hash kan överföras till dess konstruktör:

h = Hash.new(0)

h[:hi] = 1 
puts h[:hi]  # => 1 
puts h[:bye] # => 0 returns default value instead of nil

En standard kan också anges på en redan konstruerad Hash:

my_hash = { human: 2, animal: 1 }
my_hash.default = 0
my_hash[:plant] # => 0

Det är viktigt att notera att standardvärdet inte kopieras varje gång en ny nyckel öppnas, vilket kan leda till överraskande resultat när standardvärdet är en referenstyp:

# Use an empty array as the default value
authors = Hash.new([])

# Append a book title 
authors[:homer] << 'The Odyssey'

# All new keys map to a reference to the same array:
authors[:plato] # => ['The Odyssey']

För att kringgå detta problem accepterar Hash-konstruktören ett block som körs varje gång en ny nyckel öppnas och det returnerade värdet används som standard:

authors = Hash.new { [] }

# Note that we're using += instead of <<, see below
authors[:homer] += ['The Odyssey']
authors[:plato] # => []

authors # => {:homer=>["The Odyssey"]}

Observera att ovan var vi tvungna att använda + = istället för << eftersom standardvärdet inte automatiskt tilldelas hash; att använda << skulle ha lagt till i matrisen, men författare [: homer] skulle ha förblivit odefinierade:

authors[:homer] << 'The Odyssey' # ['The Odyssey']
authors[:homer] # => []
authors # => {}

För att kunna tilldela standardvärden vid åtkomst, såväl som att beräkna mer sofistikerade standardinställningar, passeras standardblocket både hash och nyckel:

authors = Hash.new { |hash, key| hash[key] = [] }

authors[:homer] << 'The Odyssey'
authors[:plato] # => []

authors # => {:homer=>["The Odyssey"], :plato=>[]}

Du kan också använda ett standardblock för att göra en åtgärd och / eller returnera ett värde beroende på nyckeln (eller annan information):

chars = Hash.new { |hash,key| key.length }

chars[:test] # => 4

Du kan till och med skapa mer komplexa hash:

page_views = Hash.new { |hash, key| hash[key] = { count: 0, url: key } }
page_views["http://example.com"][:count] += 1
page_views # => {"http://example.com"=>{:count=>1, :url=>"http://example.com"}}

För att ställa in standardvärdet till en Proc på en redan befintlig hash, använd default_proc= :

authors = {}
authors.default_proc = proc { [] }

authors[:homer] += ['The Odyssey']
authors[:plato] # => []

authors # {:homer=>["The Odyssey"]}

Skapa automatiskt en Deep Hash

Hash har ett standardvärde för nycklar som begärs men finns inte (noll):

a = {}
p a[ :b ] # => nil 

När du skapar en ny Hash kan man ange standard:

b = Hash.new 'puppy'
p b[ :b ]            # => 'puppy'

Hash.new tar också ett block, som gör att du automatiskt kan skapa kapslade hascher, till exempel Perls autovivifieringsbeteende eller mkdir -p :

# h is the hash you're creating, and k the key.
#
hash = Hash.new { |h, k| h[k] = Hash.new &h.default_proc }
hash[ :a ][ :b ][ :c ] = 3

p hash # => { a: { b: { c: 3 } } }

Ändra nycklar och värden

Du kan skapa en ny hash med tangenterna eller värdena ändrade, du kan också lägga till eller ta bort nycklar med hjälp av inject (AKA, reducera ). Till exempel för att producera en hash med strängade nycklar och stora versaler:

fruit = { name: 'apple', color: 'green', shape: 'round' }
# => {:name=>"apple", :color=>"green", :shape=>"round"}

new_fruit = fruit.inject({}) { |memo, (k,v)| memo[k.to_s] = v.upcase; memo }

# => new_fruit is {"name"=>"APPLE", "color"=>"GREEN", "shape"=>"ROUND"}

Hash är en mängd, i huvudsak en samling av nyckel- / värdepar. Därför har metoder som each , map och inject .

För varje nyckel- / värdepar i hash utvärderas det givna blocket, värdet på memo vid första körningen är frövärdet som skickas för att inject , i vårt fall en tom hash, {} . Värdet på memo för efterföljande utvärderingar är det returnerade värdet för tidigare block utvärdering, det är därför vi modifierar memo genom att ställa in en nyckel med ett värde och sedan returnera memo i slutet. Returvärdet av den slutliga block utvärderingen är returvärdet av inject , i vårt fall memo .

För att undvika att behöva ange det slutliga värdet kan du använda each_with_object istället:

new_fruit = fruit.each_with_object({}) { |(k,v), memo| memo[k.to_s] = v.upcase }

Eller till och med karta :

1,8
new_fruit = Hash[fruit.map{ |k,v| [k.to_s, v.upcase] }]

(Se detta svar för mer information, inklusive hur man manipulerar hashes på plats.)

Iterating Over a Hash

En Hash innehåller modulens Enumerable , som tillhandahåller flera iterationsmetoder, såsom: Enumerable#each , Enumerable#each_pair , Enumerable#each_key och Enumerable#each_value .

.each och .each_pair iterera över varje nyckelvärdespar:

h = { "first_name" => "John", "last_name" => "Doe" }
h.each do |key, value|
    puts "#{key} = #{value}"
end

# => first_name = John
#    last_name = Doe

.each_key upprepas endast över nycklarna:

h = { "first_name" => "John", "last_name" => "Doe" }
h.each_key do |key|
  puts key
end

# => first_name
#    last_name

.each_value iterates endast över värdena:

h = { "first_name" => "John", "last_name" => "Doe" }
h.each_value do |value|
    puts value
end

# => John
#    Doe

.each_with_index iterates över elementen och ger indexet för iterationen:

h = { "first_name" => "John", "last_name" => "Doe" }
h.each_with_index do |(key, value), index|
    puts "index: #{index} | key: #{key} | value: #{value}"
end

# => index: 0 | key: first_name | value: John
#    index: 1 | key: last_name | value: Doe

Konvertering till och från Arrays

Hashar kan konverteras fritt till och från matriser. Om du konverterar en hash av nyckel- / värdepar till en array kommer det att producera en matris som innehåller kapslade matriser för par:

{ :a => 1, :b => 2 }.to_a # => [[:a, 1], [:b, 2]]

I motsatt riktning kan en Hash skapas från en matris med samma format:

[[:x, 3], [:y, 4]].to_h # => { :x => 3, :y => 4 }

På liknande sätt kan Hashes initialiseras med Hash[] och en lista med alternerande nycklar och värden:

Hash[:a, 1, :b, 2] # => { :a => 1, :b => 2 }

Eller från en matris med två värden vardera:

Hash[ [[:x, 3], [:y, 4]] ] # => { :x => 3, :y => 4 }

Hashes kan konverteras tillbaka till en matris med alternerande nycklar och värden med flatten() :

{ :a => 1, :b => 2 }.flatten # => [:a, 1, :b, 2]

Den enkla konverteringen till och från en array gör att Hash kan fungera bra med många Enumerable metoder som collect och zip :

Hash[('a'..'z').collect{ |c| [c, c.upcase] }] # => { 'a' => 'A', 'b' => 'B', ... }

people = ['Alice', 'Bob', 'Eve']
height = [5.7, 6.0, 4.9]
Hash[people.zip(height)] # => { 'Alice' => 5.7, 'Bob' => '6.0', 'Eve' => 4.9 }

Få alla nycklar eller värden för hash

{foo: 'bar', biz: 'baz'}.keys   # => [:foo, :biz]
{foo: 'bar', biz: 'baz'}.values # => ["bar", "baz"]
{foo: 'bar', biz: 'baz'}.to_a   # => [[:foo, "bar"], [:biz, "baz"]]
{foo: 'bar', biz: 'baz'}.each   #<Enumerator: {:foo=>"bar", :biz=>"baz"}:each>

Överväga hash-funktion

Ruby hashes använder metoderna hash och eql? att utföra hashoperationen och tilldela objekt lagrade i hash till interna hashfack. Standardimplementeringen av hash i Ruby är murmur-hashfunktionen över alla medlemsfält för hash-objektet . För att åsidosätta detta beteende är det möjligt att åsidosätta hash och eql? metoder.

Som med andra hashimplementeringar kommer två objekt a och b att hashas till samma hink om a.hash == b.hash och kommer att anses vara identiska om a.eql?(b) . Således, när återimplementering av hash och eql? man bör se till att om a och b är lika med eql? de måste returnera samma hash . Annars kan detta resultera i duplicerade poster i en hash. Omvänt kan ett dåligt val i hash många objekt att dela samma hashskopa, effektivt förstöra O (1) uppslagstid och orsaka O (n) för att ringa eql? på alla objekt.

I exemplet nedan lagras endast instansen av klass A som en nyckel, eftersom den lades till först:

class A
  def initialize(hash_value)
    @hash_value = hash_value
  end
  def hash
    @hash_value # Return the value given externally
  end
  def eql?(b)
    self.hash == b.hash
  end
end

class B < A
end

a = A.new(1)
b = B.new(1)

h = {}
h[a] = 1
h[b] = 2

raise "error" unless h.size == 1
raise "error" unless h.include? b
raise "error" unless h.include? a

Filtrera haskar

select ger en ny hash med nyckelvärdespar som blocket utvärderar till true .

{ :a => 1, :b => 2, :c => 3 }.select { |k, v| k != :a && v.even? } # => { :b => 2 }

När du inte behöver nyckeln eller värdet i ett filterblock är konventionen att använda en _ på den platsen:

{ :a => 1, :b => 2, :c => 3 }.select { |_, v| v.even? } # => { :b => 2 }
{ :a => 1, :b => 2, :c => 3 }.select { |k, _| k == :c } # => { :c => 3 }

reject returnerar en ny hash med nyckelvärdespar som blocket utvärderar till false :

{ :a => 1, :b => 2, :c => 3 }.reject { |_, v| v.even? } # => { :a => 1, :c => 3 }
{ :a => 1, :b => 2, :c => 3 }.reject { |k, _| k == :b } # => { :a => 1, :c => 3 }

Ställ in operationer på Hashes

  • Korsning av Hashes

    För att få skärningspunkten mellan två hascher, returnerar du de delade nycklarna vars värden är lika:

    hash1 = { :a => 1, :b => 2 }
    hash2 = { :b => 2, :c => 3 }
    hash1.select { |k, v| (hash2.include?(k) && hash2[k] == v) } # => { :b => 2 }
    
  • Union (sammanslagning) av hashes:

    nycklar i en hash är unika, om en nyckel inträffar i båda hasherna som ska slås samman skrivs den från hash som merge på:

    hash1 = { :a => 1, :b => 2 }
    hash2 = { :b => 4, :c => 3 }
    
    hash1.merge(hash2) # => { :a => 1, :b => 4, :c => 3 }
    hash2.merge(hash1) # => { :b => 2, :c => 3, :a => 1 }
    


Modified text is an extract of the original Stack Overflow Documentation
Licensierat under CC BY-SA 3.0
Inte anslutet till Stack Overflow