Как удалить лишний уровень массива с нулевым значением из возвращаемого массива

У меня есть код почти, который дает мне то, что я хочу.

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

Спасибо!

Поскольку вы сопоставляете ключи и два ключа, если data не содержит hash.values.first, он возвращает nil. Но действительно ли вам нужен хеш с ключами, поскольку ключи уже являются ключами в данных?

iGian 23.10.2018 14:20
Структурированный массив Numpy
Структурированный массив Numpy
Однако в реальных проектах я чаще всего имею дело со списками, состоящими из нескольких типов данных. Как мы можем использовать массивы numpy, чтобы...
T - 1Bits: Генерация последовательного массива
T - 1Bits: Генерация последовательного массива
По мере того, как мы пишем все больше кода, мы привыкаем к определенным способам действий. То тут, то там мы находим код, который заставляет нас...
Что такое деструктуризация массива в JavaScript?
Что такое деструктуризация массива в JavaScript?
Деструктуризация позволяет распаковывать значения из массивов и добавлять их в отдельные переменные.
1
1
64
3

Ответы 3

Вы почти у цели, просто нужна тонкая настройка:

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

Спасибо, очень помогли!

Paul Clark 23.10.2018 15:26

Учитывая набор данных:

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

Спасибо, это потому, что мне пришлось объединить этот другой хэш бессмысленно в

Paul Clark 23.10.2018 15:25
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 нужно вычислять только один раз. Разделение проблемы на две также ускоряет тестирование.

Другие вопросы по теме