Hashie::Rash element access - ruby-on-rails

New to working with Hashie::Rash. I understand how to access elements in the "results" section of the following hashie, but how can I access the "count" element before the "results" in the following example:
hashie => #<Hashie::Rash count=20 page=1 results=[#<Hashie::Rash customer=#<Hashie::Rash addresses=[] custom_external_id="58749" emails=[#<Hashie::Rash email=#<Hashie::Rash created_at="2013-02-13T15:59:26-08:00" email="CENSORED" id=33622514 updated_at="2013-02-13T15:59:26-08:00" verified_at=nil>>] first_name="CENSORED" id=68712186 language=nil last_name="CENSORED" phones=[#<Hashie::Rash phone=#<Hashie::Rash created_at="2013-02-13T16:00:45-08:00" id=1301079 phone="CENSORED" updated_at="2013-02-13T16:00:45-08:00">>] twitters=[nil]>>, #<Hashie::Rash customer=#<Hashie::Rash addresses=[] custom_external_id="58749" emails=[] first_name="CENSORED" id=71095620 language=nil last_name="CENSORED" phones=[] twitters=[nil]>>] total=2>

Hashie::Rash still acts like a Hash for many things so #count still uses Hash#count which you can see with your result being 9.
hashie = Hashie::Rash.new({count:20, page: 1, results:["a","b","c"]})
#=> #<Hashie::Rash count=20 page=1 results=["a", "b", "c"]>
hashie.count #uses Hash#count
#=> 3
but since Hashie::Rash is at its cores still a Hash (just with Truely Indifferent Access) it also maintains quite a few based methods for access such as Hash#[]
hashie['count']
#=> 20
hashie[:count]
#=> 20
hashie['page'] == hashie.page
#=> true
Your answer works as well because Hash#values returns an Array but this seems like an unnecessary step and lacks readability and manageability.

Not particularly eloquent, but in the above example,
hashie.values[1] returns 20
Hope this helps someone else someday.

Related

Clone constant of hash into new variable without mutating constant on update with .each block?

I'm struggling with something. I've abstracted my code out to be as simple as possible, yet I still don't understand why it's having this behaviour.
I'm creating a constant consisting of a set of key-value pairs and freezing it. I'm then using the .dup method to copy the hash into a new variable.
However, when I iterate over an array and try to store it in the (previously empty) array in the new variable, it not only updates the new variable, but also the original constant. This only seems to be the case with the .each method - if I pass the new values directly as a new array, it works without updating the constant.
My abstracted code is below:
CONFIG_VALUES = { results: [], loop_count: 0 }.freeze
the_results = ["foo", "bar"]
abc = CONFIG_VALUES.dup
the_results.each do |res|
abc[:results] << res
end
abc
#=> {:results=>["foo", "bar"], :loop_count=>0}
CONFIG_VALUES
#=> {:results=>["foo", "bar"], :loop_count=>0}
Hash#dup method isn't recursive. Anyway, if you use Ruby on Rails, and I think you do since you tagged it, you can use #deep_dup method: http://api.rubyonrails.org/classes/Hash.html#method-i-deep_dup
It's an ActiveSupport method, so you could just use the gem in case you aren't using Ruby on Rails.
You can achieve the desired result with:
CONFIG_VALUES = { results: [], loop_count: 0 }.freeze
the_results = %w[foo bar]
abc = CONFIG_VALUES.merge(results: the_results)
abc
#=> {:results=>["foo", "bar"], :loop_count=>0}
CONFIG_VALUES
#=> {:results=>[], :loop_count=>0}
As I understand it, this works because #merge does not mutate CONFIG_VALUES and you are essentially creating an entirely new set of objects.

Update Each Array-Object Value in Rails

Basically I want to update each table column for a Model in Rails 5.
str = "abc---def"
str.split('---').map do |a|
Foo.where(product_id:1).update_all(bar: a)
end
Old object would be like:
[
[0] { product_id: 1,
...,
bar: "xxx",
...
},
[1] { product_id: 1,
...,
bar: "xxx",
...
}
]
New should be like:
[
[0] { product_id: 1,
...,
bar: "abc",
...
},
[1] { product_id: 1,
...,
bar: "def",
...
}
]
But what I got is bar: "def" for each. Is there a clean method in rails to achieve what I want? update_attributes gives an error.
Is the title name correct?
First of all let's get started from some basics.
You want to update multiple rows and want to set different value for each row. So it cannot be done in single query like you are doing. So you need to loop through the Foo objects and set each one separately.
So let's assume
str = "abc---def---ghi---jkl"
tokens = str.split('---')
foos_to_update = Foo.where(product_id: 1) #Let's assume it will return 4 or lesser records. (otherwise you need to tell what do you wanna do if it returns more then `tokens`)
foos_to_update.each_with_index {|foo,i| foo.update(bar: tokens[i])}
The last line is looping through returned objects and setting the bar value for each object.
First of all, using Foo.where(id:1).update_all to update a single record may work, but is non-idiomatic. It's better to use Foo.find_by(id: 1).update. For getting single records, I prefer to use find_by instead of find because it returns nil instead of raising NotFound errors, but that's a personal preference.
Second, the way you're using update_all(bar: a) is giving you unexpected results. In a map block, the returned value becomes part of the resulting array. update_all doesn't return the record which were changed. It returns an integer showing the count of records which were changed. Similarly, update doesn't return the record. It returns true or false` depending on if the validations passed.
Tying together these concepts, the following code can be written:
str = "abc---def"
str.split('---').map do |a|
foo = Foo.find_by(id:1)
foo&.update(bar: a)
foo
end
# note that you could instead write `foo.update(bar: a)` if you
# don't want to use the safe navigation operator
Or another way to write it which does the same thing:
str = "abc---def"
str.split('---').map do |a|
Foo.find_by(id:1)&.tap { |foo| foo.update(bar: a) }
end
Note that in these examples I'm using the safe navigation operator which is in Ruby versions newer than 2.3. It helps prevent NoMethodError on nil objects, but isn't really necessary.

Mapping to the Keys of a Hash

I am working with a hash called my_hash :
{"2011-02-01 00:00:00+00"=>816, "2011-01-01 00:00:00+00"=>58, "2011-03-01 00:00:00+00"=>241}
First, I try to parse all the keys, in my_hash (which are times).
my_hash.keys.sort.each do |key|
parsed_keys << Date.parse(key).to_s
end
Which gives me this :
["2011-01-01", "2011-02-01", "2011-03-01"]
Then, I try to map parsed_keys back to the keys of my_hash :
Hash[my_hash.map {|k,v| [parsed_keys[k], v]}]
But that returns the following error :
TypeError: can't convert String into Integer
How can I map parsed_keys back to the keys of my_hash ?
My aim is to get rid of the "00:00:00+00" at end of all the keys.
Why don't you just do this?
my_hash.map{|k,v| {k.gsub(" 00:00:00+00","") => v}}.reduce(:merge)
This gives you
{"2011-02-01"=>816, "2011-01-01"=>58, "2011-03-01"=>241}
There is a new "Rails way" methods for this task :)
http://api.rubyonrails.org/classes/Hash.html#method-i-transform_keys
Using iblue answer, you could use a regexp to handle this situation, for example:
pattern = /00:00:00(\+00)+/
my_hash.map{|k,v| {k.gsub(pattern,"") => v}}.reduce(:merge)
You could improve the pattern to handle different situations.
Hope it helps.
Edit:
Sorry, iblue have already posted the answer
Another alternative could be:
map
return converted two elements array [converted_key, converted_value]
convert back to a hash
irb(main):001:0> {a: 1, b: 2}.map{|k,v| [k.to_s, v.to_s]}.to_h
=> {"a"=>"1", "b"=>"2"}

Trouble on counting ActiveRecord instances in an array

I am using Ruby on Rails 3 and I would like to solve a issue counting ActiveRecord instances in an array.
I have this code
data = Account.where({:name => "Test_name", :city => "Test_city"}).limit(10)
The data debug is
#<Account:0x000001029d2da0>#<Account:0x000001029d2c60>#<Account:0x000001029d2bc0>#<Account:0x000001029d2b20>
The data inspecting is
"[#<Account name: \"Test_name\", city: \"Test_city\">, #<Account … >, #<Account id… >, …]"
Doubt: The ##<...> should be something like #<Account...>,#<Account...>,<...> (note commas)?
If in my code I use the following
data_count = data.count
The data_count is
nil
Why is it nil? How should I count accounts?
If I use result = data.class the debug of result is nil, but if I use result = data.classthe debug is "{\"inheritable_attributes\":{}}".
If I use Account.find_by_name("Test_name") instead of Account.where(...) I get same results as above.
To get to the bottom of things, start the rails console with:
$ rails c
Given that Account is an ActiveRecord model, you should be able to do the following in the rails console:
> Account.all.count
=> 100
> Account.where(:status=>'active')
=> [ #<Account id: 1, name: "a1", ...>, #<Account id: 2, name: "a2", ...>, #<Account id: 3, name: "a3", ...>, ...]
I'm doing a lot of hand waving here with ... since I don't know your schema. Replace the where condition with whatever works for your situation. The returned value should look like an array with a list of all the rows in the database that match the condition. BTW, an array is a list of element, and inspect (as well as the default display in the console) show element separated by commas. I haven't used debug so I can't comment on what it should do.
You can verify that the returned value is an AREL, and should be able to do some other operations to verify things work as expected.
> Account.where(:status=>'active').class
=> ActiveRecord::Relation
> Account.where(:status=>'active').size
=> 99
> Account.where(:status=>'active').count
=> 99
> Account.where(:status=>'active').limit(10).count
=> 10
If these work as expected in the console, there may be something in the view that is obscuring the correct behavior. In that case you'll need to post the details of your view code. If the strange behavior still occurs in the console, I would suggest posting the minimal parts of the actual model code that still exhibit the problem, along with the migration so we can see the schema.
I think you are having some problem in where condition.
Can you show the attributes value used in where clause.
For me its working fine:
data = Account.where('id != 0').limit(10)
data_count = data.count
Use the following:
data = Account.where("id = 2 and email = 'test_email#test.com'")

What kind of ruby method call is Array(x)

What is the meaning, and where is the Ruby documentation for the syntax of:
Array(phrases)
which I found browsing the Rails source here:
# File actionpack/lib/action_view/helpers/text_helper.rb, line 109
...
119: match = Array(phrases).map { |p| Regexp.escape(p) }.join('|')
I thought that Array.new would normally be used to create an array, so something different must be going on here. BTW from the context around this code, the phrases variable can be either a string or an array of strings.
It's most likely the Kernel#Array method, see here. It's slightly different than Array.new; it's more of a cast into an array. (It tries to_ary and to_a.)
Array(x) appears to act exactly the same as x.to_a.
#Brian is right - it's a method of Kernel. Pickaxe says:
Array( arg ) -> anArray
Returns arg .to_a.
Array(1..5) » [1, 2, 3, 4, 5]
It's the Kernel#Array method, as others have already stated.
But the Ruby documentation does not give credit to this method's usefulness in simplifying your code. Also it does not tell you that objects which don't have a to_ary or a to_a method are encapsulated in an array.
Array([1,2,3]) -> [1,2,3]
Array(1..3) -> [1,2,3]
Array({ a: 1, b: 2 }) -> [[:a, 1],[:b,2]]
Array("Hello World") -> ["Hello World"]
Array(1) -> [1]
All these features of Kernel#Array allow you to handle typical corner cases with parameters in one single line.
See this code, which is a typical situation in many APIs or DSLs:
# data can be nil, a single value or an array
def handle(data)
data ||= Array.new #Case 1: Data is nil
data = [data] unless data.is_a?(Array) #Case 2: Data is a single value
data.each { |d| ... }
end
This can be simplified by using Kernel#Array:
def handle(data)
Array(data).each { |d| ... }
end
Of course one has to be careful with providing different types for the data parameter, because the to_ary/to_a methods might or might not give you what you expect.

Resources