У меня есть код почти, который дает мне то, что я хочу.
def merge_data(keys, data)
merged_data = keys.map {|hash| data.first.map {|k,v| if hash.values.first == k then hash.merge(v) end}}
end
См. Ниже разницу между ожидаемым (первым) и фактическим возвращаемым значением:
-[{:awesomeness=>10,
- :first_name=>"blake",
- :height=>"74",
- :last_name=>"johnson"},
- {:awesomeness=>9, :first_name=>"ashley", :height=>60, :last_name=>"dubs"}]
+[[{:awesomeness=>10,
+ :first_name=>"blake",
+ :height=>"74",
+ :last_name=>"johnson"},
+ nil],
+ [nil,
+ {:awesomeness=>9, :first_name=>"ashley", :height=>60, :last_name=>"dubs"}]]
Если бы кто-нибудь мог объяснить, почему я получаю дополнительный уровень массива с нулевым индексом, я был бы очень признателен!
Основываясь на данных ниже:
let(:keys) {[
{:first_name => "blake"},
{:first_name => "ashley"}
]}
let(:data) {[
{"blake" => {
:awesomeness => 10,
:height => "74",
:last_name => "johnson"},
"ashley" => {
:awesomeness => 9,
:height => 60,
:last_name => "dubs"}
}
]}
let(:merged_data) {[
{:first_name => "blake",
:awesomeness => 10,
:height => "74",
:last_name => "johnson"},
{:first_name => "ashley",
:awesomeness => 9,
:height => 60,
:last_name => "dubs"}
]}
Спасибо!
Вы почти у цели, просто нужна тонкая настройка:
def merge_data(keys, data)
merged_data = keys.map {|hash| data.first.map {|k,v| if hash.values.first == k then hash.merge(v) end }.compact[0] }
end
keys = [{:first_name => "blake"}, {:first_name => "ashley"}]
data = [
{"blake" => {
:awesomeness => 10,
:height => "74",
:last_name => "johnson"},
"ashley" => {
:awesomeness => 9,
:height => 60,
:last_name => "dubs"}
}]
puts merge_data(keys, data).inspect
#=>
[
{
:first_name=>"blake",
:awesomeness=>10,
:height=>"74",
:last_name=>"johnson"},
{
:first_name=>"ashley",
:awesomeness=>9,
:height=>60,
:last_name=>"dubs"
}
]
Что было изменено ??
Добавлен .compact[0]
во внутренний блок.
Почему? Потому что это map
, работающий с каждым ключом и каждым значением данных. Возвращение nil для одного, не совпадающего с другим, и возвращение каждый раз массива вместо объекта Hash
.
Однако мы можем создать собственный класс и реконструировать данные позже:
class Person
attr_reader :first_name, :last_name, :height, :awesomeness
def initialize(first_name)
@first_name = first_name
end
def update(info)
info.each do |key, value|
instance_variable_set("@#{key}", value) if respond_to?(key)
end
end
def to_h
{ first_name: self.first_name, last_name: self.last_name, awesomeness: self.awesomeness, height: self.height }
end
end
people = keys.each_with_object({}) do |k, person_map|
person_map[k[:first_name]] = Person.new(k[:first_name])
end
data.each do |people_info|
people_info.each do |first_name, info|
if people[first_name]
people[first_name].update(info)
end
end
end
p people.values.map{ |p| p.to_h }
#=> [{:first_name=>"blake", :last_name=>"johnson", :awesomeness=>10, :height=>"74"}, {:first_name=>"ashley", :last_name=>"dubs", :awesomeness=>9, :height=>60}]
Спасибо, очень помогли!
Учитывая набор данных:
data = {
"blake" => {
:awesomeness => 10,
:height => "74",
:last_name => "johnson"},
"ashley" => {
:awesomeness => 9,
:height => 60,
:last_name => "dubs"}
}
Почему не так?
data.map { |h| h.last.merge({first_name: h.first}) }
#=> [{:awesomeness=>10, :height=>"74", :last_name=>"johnson", :first_name=>"blake"}, {:awesomeness=>9, :height=>60, :last_name=>"dubs", :first_name=>"ashley"}]
Итак, если я хорошо понимаю вашу структуру данных, попробуйте Перечислимый # each_with_object:
def merge_data(keys, data)
keys.each_with_object([]) { |hash, obj| data.first.map { |k,v| if hash.values.first == k then obj << hash.merge(v) end } }
end
keys = [
{:first_name => "blake"},
{:first_name => "ashley"}
]
data = [{
"blake" => {
:awesomeness => 10,
:height => "74",
:last_name => "johnson"},
"ashley" => {
:awesomeness => 9,
:height => 60,
:last_name => "dubs"}
}]
merge_data(keys, data)
#=> [{:first_name=>"blake", :awesomeness=>10, :height=>"74", :last_name=>"johnson"}, {:first_name=>"ashley", :awesomeness=>9, :height=>60, :last_name=>"dubs"}]
Спасибо, это потому, что мне пришлось объединить этот другой хэш бессмысленно в
keys = [{ :first_name=> "ashley" }, { :first_name=>"blake" }]
data = [{ "blake" => { :awesomeness=>10, :height=>"74", :last_name=>"johnson" },
"margo" => { :awesomeness=>30, :height=>"63", :last_name=>"magpie" },
"ashley" => { :awesomeness=>9, :height=>"60", :last_name=>"dubs" } }]
Я предлагаю сделать это в два этапа. Первый - построить более подходящую структуру данных для содержимого data
, а именно хэш:
data_adj = data.reduce(&:merge)
#=> {"blake" =>{:awesomeness=>10, :height=>"74", :last_name=>"johnson"},
# "margo" =>{:awesomeness=>30, :height=>"63", :last_name=>"magpie"},
# "ashley"=>{:awesomeness=>9, :height=>"60", :last_name=>"dubs"}}
Второй - объединить хеши в keys
с соответствующими значениями data_adj
:
keys.map { |h| h.merge(data_adj[h[:first_name]]) }
#=> [{:first_name=>"ashley", :awesomeness=>9, :height=>"60", :last_name=>"dubs"},
# {:first_name=>"blake", :awesomeness=>10, :height=>"74", :last_name=>"johnson"}]
Этот подход был бы особенно эффективным, если бы операцию слияния нужно было выполнять повторно для разных значений keys
, поскольку data_adj
нужно вычислять только один раз. Разделение проблемы на две также ускоряет тестирование.
Поскольку вы сопоставляете ключи и два ключа, если
data
не содержитhash.values.first
, он возвращаетnil
. Но действительно ли вам нужен хеш с ключами, поскольку ключи уже являются ключами в данных?