web-dev-qa-db-de.com

Wie kann ich ein Array von Hashwerten filtern, um nur die Schlüssel in einem anderen Array zu erhalten?

Ich versuche, eine Teilmenge von Schlüsseln für jeden Hash in einem Array abzurufen. 

Die Hashes sind tatsächlich viel größer, aber ich dachte, das ist einfacher zu verstehen:

[
  {
    id:2,
    start: "3:30",
    break: 30,
    num_attendees: 14
  },
  {
    id: 3,
    start: "3: 40",
    break: 40,
    num_attendees: 4
  },
  {
    id: 4,
    start: "4: 40",
    break: 10,
    num_attendees: 40
  }
]

Ich möchte nur die Werte id und start erhalten.

Ich habe es versucht:

return_keys = ['id','start']
return_array = events.select{|key,val|  key.to_s.in? return_keys}

dies gibt jedoch ein leeres Array zurück. 

27
pedalpete

Dies sollte tun, was Sie wollen:

events.map do |hash|
  hash.select do |key, value|
    [:id, :start].include? key
  end
end

Möglicherweise schnellere (aber etwas weniger hübsche) Lösung:

events.map do |hash|
  { id: hash[:id], start: hash[:start] }
end

Wenn Sie return_keys benötigen, um dynamisch zu sein:

return_keys = [:id, :start]
events.map do |hash|
  {}.tap do |new_hash|
    return_keys.each do |key|
      new_hash[key] = hash[key]
    end
  end
end

Beachten Sie, dass select in Ihrem Code Elemente aus dem array auswählt, da Sie dies so bezeichnet haben, die im Array enthaltenen Hashes jedoch nicht ändern.

Wenn Sie über die Leistung besorgt sind, habe ich alle hier aufgeführten Lösungen ( code ) bewertet:

                user     system      total        real
amarshall 1  0.140000   0.000000   0.140000 (  0.140316)
amarshall 2  0.060000   0.000000   0.060000 (  0.066409)
amarshall 3  0.100000   0.000000   0.100000 (  0.101469)
tadman 1     0.140000   0.010000   0.150000 (  0.145489)
tadman 2     0.110000   0.000000   0.110000 (  0.111838)
mu           0.130000   0.000000   0.130000 (  0.128688)
43
Andrew Marshall

Wenn Sie Rails verwenden (oder nichts dagegen haben, ActiveSupport ganz oder teilweise einzubeziehen), können Sie Hash#slice verwenden:

return_array = events.map { |h| h.slice(:id, :start) }

Hash#slice leistet zusätzliche Arbeit unter der Decke, aber es ist wahrscheinlich schnell genug, dass Sie es für kleine Hashes nicht bemerken werden und die Klarheit ist ganz nett.

31
mu is too short

Eine bessere Lösung ist, einen Hash als Index zu verwenden, anstatt für jeden Schlüssel eine lineare Array-Suche durchzuführen:

events = [{id:2, start:"3:30",break:30,num_attendees:14},{id:3, start:"3:40",break:40,num_attendees:4},{id:4, start:"4:40",break:10,num_attendees:40}]

return_keys = [ :id, :start ]

# Compute a quick hash to extract the right values: { key => true }
key_index = Hash[return_keys.collect { |key| [ key, true ] }]

return_array = events.collect do |event|
  event.select do |key, value|
    key_index[key]
  end
end

# => [{:id=>2, :start=>"3:30"}, {:id=>3, :start=>"3:40"}, {:id=>4, :start=>"4:40"}]

Ich habe dies so eingestellt, dass Symbole als Schlüsselnamen verwendet werden, die Ihrer Definition von events entsprechen.

Dies kann weiter verbessert werden, indem der return_keys als direkter Treiber verwendet wird:

events = [{id:2, start:"3:30",break:30,num_attendees:14},{id:3, start:"3:40",break:40,num_attendees:4},{id:4, start:"4:40",break:10,num_attendees:40}]

return_keys = [ :id, :start ]

return_array = events.collect do |event|
  Hash[
    return_keys.collect do |key|
      [ key, event[key] ]
    end
  ]
end

Das Ergebnis ist das gleiche. Wenn die extrahierte Teilmenge tendenziell viel kleiner als das Original ist, ist dies möglicherweise die beste Vorgehensweise.

2
tadman

In Anbetracht dessen, dass die Effizienz ein Problem zu sein scheint, würde ich folgendes vorschlagen.

Code

require 'set'

def keep_keys(arr, keeper_keys)
  keepers = keeper_keys.to_set
  arr.map { |h| h.select { |k,_| keepers.include?(k) } }
end

Dies verwendet Hash # select , was im Gegensatz zu Enumerable # select einen Hash zurückgibt. Ich habe keeper_keys in einen Satz für schnelle Suchvorgänge konvertiert.

Beispiele

arr = [{ id:2, start: "3:30", break: 30 },
       { id: 3, break: 40, num_attendees: 4 },
       { break: 10, num_attendees: 40 }]

keep_keys arr, [:id, :start]
  #=> [{:id=>2, :start=>"3:30"}, {:id=>3}, {}] 
keep_keys arr, [:start, :break]
  #=> [{:start=>"3:30", :break=>30}, {:break=>40}, {:break=>10}] 
keep_keys arr, [:id, :start, :cat]
  #=> [{:id=>2, :start=>"3:30"}, {:id=>3}, {}] 
keep_keys arr, [:start]
  #=> [{:start=>"3:30"}, {}, {}] 
keep_keys arr, [:cat, :dog]
0
Cary Swoveland