I have an instance of an object that is created like this:
Example.create(:attrib0 => {
:attrib1 => value,
:attrib2 => [
{:attrib3 => value},
{:attrib4 => value}
]
})
How can I access :attrib4?
You should use serialize in your model, then you'll be able to return the hash correctly:
class SomeModel < ActiveRecord::Base
serialize :attrib0
end
Then the following should return the hash
hash = #model.attrib0
# => {:attrib1 => value, :attrib2 => [{:attrib3 => value}, {:attrib4 => value}]
# now to access attrib4 you need to get the attrib2 array,
# then grab attrib4 by its index:
hash[:attrib2][1]
# => {:attrib4 => value}
# or to get the value:
hash[:attrib2][1][:attrib4]
# => value
The above however can get quite complex and ugly, which is why I recommended creating another model for these attributes instead.
I think you should use nested attributes. Here's how it can be:
class Example
has_one :attrib0
accepts_nested_attributes_for :attrib0
end
params = { :attrib0 => { :attrib1 => value1,
:attrib2 => [ {:attrib3 => value3}, {:attrib4 => value4} ] }
}
example = Example.create(params[:attrib0])
example.attrib0.attrib1 #=> value1
example.attrib0.attrib2 #=> [ {:attrib3 => value3}, {:attrib4 => value4} ]
Using Ruby technique only:
h = {:attrib0 => {
:attrib1 => :value1,
:attrib2 => [
{:attrib3 => :value2},
{:attrib4 => :value3}
]
}}
p h[:attrib0][:attrib2].last[:attrib4] #=> :value3
Related
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"}
# ]
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"}]]]
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 ....
i'm new to ruby and i want to instersect two arrays
validAccountTypes = [
'Asset' => 'Asset',
'Liability' => 'Liability',
'Equity' => 'Equity',
'Income' => 'Income',
'CostOfSales' => 'Cost of Sales',
'Expense' => 'Expenses',
'OtherIncome' => 'Other Income',
'OtherExpense' => 'Other Expenses',
]
types = [
'Asset',
'Other Income',
'Other Expenses',
]
I want a result of valid Accounts with keys base on array types. The output would be
[{"Asset"=>"Asset", "OtherIncome"=>"Other Income", "OtherExpense" => "Other Expenses"}]
Is it possible without a loop?
Here's a rewrite of your variables with a few changes:
Underscore variable names
valid_account_types is now a hash instead of an array containing a hash.
Some typos corrected so that the members of types match keys of valid_account_types.
valid_account_types = {
'Asset' => 'Asset',
'Liability' => 'Liability',
'Equity' => 'Equity',
'Income' => 'Income',
'CostOfSales' => 'Cost of Sales',
'Expense' => 'Expenses',
'OtherIncome' => 'Other Income',
'OtherExpenses' => 'Other Expenses',
}
types = [
'Asset',
'OtherIncome',
'OtherExpenses',
]
Given that setup, if you are using Rails, you can get the result you want using Hash#slice, like this:
> valid_account_types.slice(*types)
=> {"Asset"=>"Asset", "OtherIncome"=>"Other Income", "OtherExpenses"=>"Other Expenses"}
Note that Hash#slice does not exist in Ruby itself. If you want to do this in plain-old Ruby, you could check out the implementation in i18n:
class Hash
def slice(*keep_keys)
h = self.class.new
keep_keys.each { |key| h[key] = fetch(key) if has_key?(key) }
h
end
end
Your first array isn't an array, it's actually a hash (notice the surrounding braces, rather than brackets):
validAccountTypes = {
'Asset' => 'Asset',
'Liability' => 'Liability',
'Equity' => 'Equity',
'Income' => 'Income',
'CostOfSales' => 'Cost of Sales',
'Expense' => 'Expenses',
'OtherIncome' => 'Other Income',
'OtherExpense' => 'Other Expenses',
}
You can get the keys on the hash and intersect it with your array:
common_keys = validAccountTypes.keys & types
Then you can select only those keys:
# Hash#slice works in Rails:
validAccountTypes.slice(*common_keys)
# Otherwise use Hash#select:
validAccountTypes.select { |key, value| common_keys.include?(key) }
Note that in your example, your hash keys include 'OtherIncome' and 'OtherExpense', but your types array includes 'Other Income' and 'Other Expenses'. Your array values should match the keys from the hash.
I have the method:
def self.store(params)
params.each { }
end
It works perfectly, if I pass an Array of Hashes:
params = [ { key: 'value' }, { key: 'value' } ]
However, I might want to pass only a single Hash, instead of an Array of Hashes:
params = { key: 'value' }
What would be the cleanest Ruby way to convert a Hash into an Array of Hashes?
The Array() method a kind of ensures, that an array is always returned, but when the Hash is passed, it is converted into an Array itself.
Array({ key: 'value' }) => [[:key, 'value']]
What I need:
{ key: 'value' } => [ { key: 'value' } ]
Is there any nice way to implement this, or do I have to do a manual type checking with is_a?(Array) ?
For me, the best solution is to change the method to:
def self.store(*hashes)
params = hashes.flatten
puts params.inspect
end
If you pass a single hash, it will be an array
If you pass an array of hashes, it remains the same
If you pases N hashes, it compacts all parameters into a one dimensional array.
You can pass whatever you want.
self.store({:key => 'value'}) # => [{:key => 'value'}]
self.store({:key => 'value'}, {:foo => 'bar'}) # => [{:key => 'value'}, {:foo => 'bar'}]
self.store([{:key => 'value'}, {:foo => 'bar'}]) # => [{:key => 'value'}, {:foo => 'bar'}]
Try this
def self.store(params)
params = [params].flatten
...
end
I would do it like this:
def self.store(params)
(params.is_a?(Array) ? params : [params]).each {|single_hash| }
end
This is what I've been getting:
{:user=>{:employees=>{...}, :login=>"dernalia", :id=>1, :role=>2}}
What is generating the hash:
def management_tree(args = {})
args = {:users => [], :result => {}}.merge(args) #defaults
result = args[:result]
if not args[:users].include? self.login #prevent duplicates
result.merge!({:user => {:id => self.id,
:login => self.login,
:role => self.role,
:employees => employee_tree(args[:users] + [self.login], args[:result])
}
})
end
logger.info result.inspect
return result
end
def employee_tree(users, result)
if self.employees.length > 0
self.employees.each {|emp| (emp.management_tree({:users => users, :result => result})) }
end
return result
end
Now... it's supposed to return something like this:
{:user=>{:login=>"me", :id=>1, :role=>2,
:employees=>{
:user => {:login => "2", ...},
:user => {:login => "3",
:employees => {...}
}
}}
Some console output:
% bundle exec script/console
Loading development environment (Rails 2.3.8)
>> require "awesome_print"
=> []
>> ap User.find(1).management_tree[:employees]
nil
=> nil
>> ap User.find(1).management_tree
{
:user => {
:employees => {...},
:role => 2,
:login => "me",
:id => 1
}
}
=> {:user=>{:employees=>{...}, :role=>2, :login=>"me", :id=>1}}
>>
now... it says that employees is nil... but it shouldn't be... it should have 3 hashes ... =\
but also, what does {...} mean? it seams terribly ambiguous
Ruby is clever about recursive structures and will use "..." instead of looping indefinitely.
For example:
a = [1, 2]
a << a # a is now recursive, since it contains itself
a.to_s # => [1, 2, [...]]
a[2][2][2][2][2][2][2] == a # => true
In your case, the {...} refers to any of the hashes already in the process of being outputed.
Maybe what you meant to do was to insert a copy of a hash? In the simple array example:
a = [1, 2]
a << a.dup # a is not recursive
a.to_s # => [1, 2, [1, 2]]
How am I able to create a hash within a hash, with the nested hash having a key to indentify it. Also the elements that I create in the nested hash, how can I have keys for them as well
for example
test = Hash.new()
#create second hash with a name?? test = Hash.new("test1")??
test("test1")[1] = 1???
test("test1")[2] = 2???
#create second hash with a name/key test = Hash.new("test2")???
test("test2")[1] = 1??
test("test2")[2] = 2??
thank you
Joel's is what I would do, but could also do this:
test = Hash.new()
test['test1'] = Hash.new()
test['test1']['key'] = 'val'
my_hash = { :nested_hash => { :first_key => 'Hello' } }
puts my_hash[:nested_hash][:first_key]
$ Hello
or
my_hash = {}
my_hash.merge!(:nested_hash => {:first_key => 'Hello' })
puts my_hash[:nested_hash][:first_key]
$ Hello
h1 = {'h2.1' => {'foo' => 'this', 'cool' => 'guy'}, 'h2.2' => {'bar' => '2000'} }
h1['h2.1'] # => {'foo' => 'this', 'cool' => 'guy'}
h1['h2.2'] # => {'bar' => '2000'}
h1['h2.1']['foo'] # => 'this'
h1['h2.1']['cool'] # => 'guy'
h1['h2.2']['bar'] # => '2000'