web-dev-qa-db-fra.com

Comment filtrer un tableau de hachages pour obtenir uniquement les clés d'un autre tableau?

J'essaie d'obtenir un sous-ensemble de clés pour chaque hachage d'un tableau. 

Les hachages sont en réalité beaucoup plus gros, mais je me suis dit que c'était plus facile à comprendre:

[
  {
    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
  }
]

Je veux obtenir uniquement les valeurs id et start.

J'ai essayé:

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

mais cela retourne un tableau vide. 

27
pedalpete

Cela devrait faire ce que vous voulez:

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

Solution potentiellement plus rapide (mais un peu moins jolie):

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

Si vous avez besoin de return_keys pour être dynamique:

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

Notez que, dans votre code, select choisit des éléments dans array, puisque vous l'appelez ainsi, mais ne change pas les hachages contenus dans le tableau.

Si vous êtes préoccupé par les performances, j'ai analysé toutes les solutions répertoriées ici ( code ):

                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

S'il vous arrive d'utiliser Rails (ou si vous avez intérêt à intégrer tout ou partie d'ActiveSupport), vous pouvez utiliser Hash#slice :

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

Hash#slice fait un peu plus de travail sous les couvertures, mais il est probablement assez rapide pour ne pas le remarquer pour les petits hachages et la clarté est plutôt agréable.

31
mu is too short

Une meilleure solution consiste à utiliser un hachage comme index au lieu d'effectuer une recherche de tableau linéaire pour chaque clé:

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"}]

J'ai ajusté cette option pour utiliser des symboles en tant que noms de clé correspondant à votre définition de events.

Cela peut être encore amélioré en utilisant le return_keys en tant que pilote direct:

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

Le résultat est le même. Si le sous-ensemble que vous extrayez a tendance à être beaucoup plus petit que l'original, c'est peut-être la meilleure approche.

2
tadman

Considérant que l'efficacité semble être une préoccupation, je suggérerais ce qui suit.

Code

require 'set'

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

Ceci utilise Hash # select , qui, contrairement à Enumerable # select , renvoie un hachage. J'ai converti keeper_keys en un ensemble permettant des recherches rapides.

Exemples

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