Как я могу отфильтровать массив хешей, чтобы получить только ключи в другом массиве?

208
19

Я пытаюсь получить подмножество ключей для каждого хэша в массиве.


Хеши на самом деле намного больше, но я понял, что это проще понять:


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

Я хочу получить только значения id и start.


Я пробовал:


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

но это возвращает пустой массив.

спросил(а) 2012-03-02T20:57:00+04:00 8 лет, 8 месяцев назад
1
Решение
260

Это должно делать то, что вы хотите:


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

Потенциально быстрее (но несколько менее симпатичное) решение:


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

Если вам нужно return_keys быть динамическим:

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

Обратите внимание, что в вашем коде select выделяет элементы из массива, начиная с того, на что вы его называли, но не меняет хэши, содержащиеся в массиве.


Если вы обеспокоены производительностью, я сравнил все перечисленные здесь решения (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)

ответил(а) 2012-03-02T21:01:00+04:00 8 лет, 8 месяцев назад
220

Если вы используете Rails (или не против втягивания всего или части ActiveSupport), вы можете использовать Hash#slice:


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

Hash#slice выполняет некоторую дополнительную работу под обложками, но, вероятно, достаточно быстро, чтобы вы не заметили ее для небольших хэшей, и ясность довольно приятная.

ответил(а) 2012-03-02T21:48:00+04:00 8 лет, 8 месяцев назад
71

Лучшим решением является использование хэша в качестве вашего индекса вместо выполнения поиска по линейному массиву для каждого ключа:


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


Я скорректировал это, чтобы использовать символы в качестве имен ключей в соответствии с вашим определением events.


Это можно улучшить, используя return_keys в качестве прямого драйвера:


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


Результат тот же. Если подмножество, которое вы извлекаете, как правило, намного меньше оригинала, это может быть наилучшим подходом.

ответил(а) 2012-03-02T21:21:00+04:00 8 лет, 8 месяцев назад
41

Учитывая, что эффективность, похоже, вызывает беспокойство, я бы предложил следующее.


код


require 'set'

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


Здесь используется Hash # select, который, в отличие от Enumerable # select, возвращает хэш. Я преобразовал keeper_keys в набор для быстрого поиска.


<сильные > Примеры


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]

ответил(а) 2016-02-16T11:21:00+03:00 4 года, 9 месяцев назад
Ваш ответ
Введите минимум 50 символов
Чтобы , пожалуйста,
Выберите тему жалобы:

Другая проблема