Antonio Falcetta
Antonio Falcetta

Reputation: 11

TypeError no implicit conversion of Symbol into Integer

Hash

data = {
  :recordset => {
    :row => {
      :property => [
        {:name => "Code",     :value => "C0001"}, 
        {:name => "Customer", :value => "ROSSI MARIO"}
      ]
    }
  },
  :@xmlns => "http://localhost/test"
}

Code Used

result = data[:recordset][:row].each_with_object([]) do |hash, out|
           out << hash[:property].each_with_object({}) do |h, o|
                    o[h[:name]] = h[:value]
                  end
         end

I cannot get the following output:

[{"Code"=>"C0001", "Customer"=>"ROSSI MARIO", "Phone1"=>"1234567890"}

Error message:

TypeError no implicit conversion of Symbol into Integer

It works correctly in case of multi records

data = {
  :recordset => {
    :row => [{
      :property => [
        {:name => "Code",     :value => "C0001"},
        {:name => "Customer", :value => "ROSSI MARIO"},
        {:name => "Phone1",   :value => "1234567890"}
      ]
    }, {
      :property => [
        {:name => "Code",     :value => "C0002"},
        {:name => "Customer", :value => "VERDE VINCENT"},
        {:name => "Phone1",   :value => "9876543210"},
        {:name => "Phone2",   :value => "2468101214"}
      ]
    }]
  },
  :@xmlns => "http://localhost/test"
}

Code used

data.keys
#=> [:recordset, :@xmlns] 

data[:recordset][:row].count
#=> 2   # There are 2 set of attribute-value pairs


result = data[:recordset][:row].each_with_object([]) do |hash, out|
           out << hash[:property].each_with_object({}) do |h, o|
                    o[h[:name]] = h[:value]
                  end
         end
#=> [
#   {"Code"=>"C0001", "Customer"=>"ROSSI MARIO", "Phone1"=>"1234567890"},
#   {"Code"=>"C0002", "Customer"=>"VERDE VINCENT", "Phone1"=>"9876543210", "Phone2"=>"2468101214"}
# ] 

Upvotes: 0

Views: 6151

Answers (2)

Simple Lime
Simple Lime

Reputation: 11035

In the first case data[:recordset][:row] is not an Array, it's a Hash, so when you iterate it, the hash variable becomes the array:

[:property, [{:name=>"Code", :value=>"C0001"}, {:name=>"Customer", :value=>"ROSSI MARIO"}]]

In the second case, it's an Array, not a Hash, so when you iterate it, it becomes the hash:

{:property=>[{:name=>"Code", :value=>"C0001"}, {:name=>"Customer", :value=>"ROSSI MARIO"}, {:name=>"Phone1", :value=>"1234567890"}]}

You're always assuming it's the second format. You could force it into an array, and then flatten by 1 level to treat both instances the same:

result = [data[:recordset][:row]].flatten(1).each_with_object([]) do |hash, out|
  out << hash[:property].each_with_object({}) do |h, o|
    o[h[:name]] = h[:value]
  end
end

# => [{"Code"=>"C0001", "Customer"=>"ROSSI MARIO"}] # result from example 1
# => [{"Code"=>"C0001", "Customer"=>"ROSSI MARIO", "Phone1"=>"1234567890"},
#     {"Code"=>"C0002", "Customer"=>"VERDE VINCENT",
#      "Phone1"=>"9876543210", "Phone2"=>"2468101214"}] # result from example 2

It's tempting to try and use Kernal#Array() instead of [].flatten(1), but you have to remember that Hash implements to_a to return a nested array of keys and values, so Kernal#Array() doesn't work like you'd want it to:

Array(data[:recordset][:row]) # using the first example data
# => [[:property, [{:name=>"Code", :value=>"C0001"}, {:name=>"Customer", :value=>"ROSSI MARIO"}]]]

Upvotes: 5

arieljuod
arieljuod

Reputation: 15838

You can create an array if it's not an array to normalize the input before processing it.

info = data[:recordset][:row]
info = [info] unless info.is_an? Array
result = info.each_with_object([]) do ....

Upvotes: 0

Related Questions