I've to build a massive JSON file based on few different API calls. I want to use Dry::Struct To easily manipulate data like:
obj = Test::DataStruct.new
obj.send_envelope_description = 1
but expected output should be camelcase:
#<Test::DataStruct SendEnvelopeDescription=1>
Additionally, passed data into Test::DataStruct obj should accept camelcase keys like:
data = { SendEnvelopeDescription: 1 }
test = TestDataStruct.new(data)
=> #<Test::DataStruct SendEnvelopeDescription=1>
I tried to use transform_keys { |key| key.to_s.camelize } but I'm getting an error:
3.1.2 :045 > obj = Test::DataStruct.new(send_envelope_description: 1)
`block in resolve_missing_keys': [Test::DataStruct.new] :send_envelope_description is missing in Hash input (Dry::Struct::Error)
`block in resolve_missing_keys': :send_envelope_description is missing in Hash input (Dry::Types::MissingKeyError)
Is it possible to do so?
Related
I'm trying to use the AutoML prediction service from the Ruby gem google-cloud-automl and I keep getting errors. I already have a deployed model working with the online predictions.
Here is my current code:
payload = {
row: {
column_spec_ids: %w(COLUMN_NUMBER_1 COLUMN_NUMBER_2 COLUMN_NUMBER_3 COLUMN_NUMBER_4),
values: [
DATA_1,
DATA_2,
DATA_3,
DATA_4
]
}
}
client = Google::Cloud::AutoML::Prediction.new(version: :v1beta1)
response = client.predict(formatted_model_path, payload)
and I receive this error:
Google::Protobuf::TypeError: Invalid type String to assign to submessage field ''.
from path/to/my/vendor/bundle/ruby/2.5.0/gems/google-gax-1.8.1/lib/google/gax/util.rb:65:in `initialize'
(In my code, COLUMN_NUMBER_1 to _4 are actually real ID ant DATA_1 to _4 are strings)
formatted_model_path is the path of my model. I was able to access an NLP model earlier with this code (I only updated the payload format)
I've already tried to generate a Google::Cloud::AutoML::V1beta1::Row . I'm able to fill the column_spec_ids but every time I try this code
request = Google::Cloud::AutoML::V1beta1::Row.new
request.values = payload[:row][:values]
I get this error
Google::Protobuf::TypeError: Expected repeated field array
from (pry):4:in `method_missing'
I actually found the solution...
You need to provide the kind of data you are passing.
Instead of
values: [
DATA_1,
DATA_2,
DATA_3,
DATA_4
]
I should have
values: [
{string_value: DATA_1},
{string_value: DATA_2},
{string_value: DATA_3},
{string_value: DATA_4}
]
(and you can have string_value, number_value and some other kind, i think the full list is here)
I am new to Rails, and working with some JSON, and not sure how to get to the data as the examples below:
1) If i were to use JSON.parse(response)['Response']['test']['data']['123456'], i will need to parse another response for 123457, is there a better way to loop through all the objects in data?
2) base on the membershipId, identify the top level object, ie data.
"test": {
"data": {
"123456": {
"membershipId": "321321312",
"membershipType": a,
},
"123457": {
"membershipId": "321321312",
"membershipType": a,
},
}
JSON.parse(response)['Response']['test']['data'].each do |key, object|
puts key
puts object['membershipID']
...
end
To select the data record associated with a particular membership
match_membership = '321321312'
member = JSON.parse(response)['Response']['test']['data'].select |_key, object|
object['membershipID'] == match_membership
end
puts member.key
=> 123456
For 1:
Assumption:
By you saying "need to parse another response", you were doing something like below:
# bad code: because you are parsing `response` multiple times
JSON.parse(response)['Response']['test']['data']['123456']
JSON.parse(response)['Response']['test']['data']['123457']
then simply:
Solution 1:
If you are gonna be accessing 2+ level deep hash values for just maybe 2 or 3 times,
response_hash = JSON.parse(response)
response_hash['Response']['test']['data']['123456']
response_hash['Response']['test']['data']['123457']
Solution 2:
If you are gonna be accessing 2+ level deep hash values for loooooots of times,
response_hash = JSON.parse(response)
response_hash_response_test_data = response_hash['Response']['test']['data']
response_hash_response_test_data['123456']
response_hash_response_test_data['123457']
response_hash_response_test_data['123458']
response_hash_response_test_data['123459']
response_hash_response_test_data['123460']
# ...
Solution 2 is better than Solution 1 because it saves repetitive method calls for Hash#[] which is the "getter" method each time you do like ...['test'] then ['data'] then ['123456'], and so is better-off doing Solution 2 which you store the nested-level of the hash into a variable (this does not duplicate the values in-memory!). Plus it's more readable this way.
I have the following hash. Using ruby, I want to get the value of "runs". I can't figure out how to do it. If I do my_hash['entries'], I can dig down that far. If I take that value and dig down lower, I get this error:
no implicit conversion of String into Integer:
{"id"=>2582, "entries"=>[{"id"=>"7", "runs"=>[{"id"=>2588, ...
Assuming that you want to lookup values by id, Array#detect comes to the rescue:
h = {"id"=>2582, "entries"=>[{"id"=>"7", "runs"=>[{"id"=>2588}]}]}
# ⇓⇓⇓⇓⇓⇓⇓ lookup element with id = 7
h['entries'].detect { |e| e['id'] == 7 }['runs']
.detect { |e| e['id'] == 2588 }
#⇒ { "id" => 2588 }
As you have an array inside the entries so you can access it using an index like this:
my_hash["entries"][0]["runs"]
You need to follow the same for accessing values inside the runs as it is also an array.
Hope this helps.
I'm not sure about your hash, as it's incomplete. So , guessing you have multiple run values like:
hash = {"id"=>2582, "entries"=>[{"id"=>"7", "runs"=>[{"id"=>2588}]},
{"id"=>"8", "runs"=>[{"id"=>2589}]},
{"id"=>"9", "runs"=>[{"id"=>2590}]}]}
Then, you can do
hash["entries"].map{|entry| entry["runs"]}
OUTPUT
[[{"id"=>2588}], [{"id"=>2589}], [{"id"=>2590}]]
I want to break down a JSON string into smaller objects. I have two servers, one acting as the web-app interface to the whole application and the other is a repository/database.
I'm able to retrieve information from the repository to the web-app as JSON, but after that I don't know how to return it.
Here's a sample of the JSON being returned:
{"respPages":[{"page":{"page_url":"http://www.google.com/","created_at":"2011-08-10T11:00:19Z","website_id":1,"updated_at":"2011-08-10T11:00:19Z","id":1}},{"page":{"page_url":"http://www.blank.com/services/content_services/","created_at":"2011-08-10T11:02:46Z","website_id":1,"updated_at":"2011-08-10T11:02:46Z","id":2}}],"respSite":{"website":{"created_at":"2011-08-10T11:00:19Z","website_id":null,"updated_at":"2011-08-10T11:00:19Z","website_url":null,"id":1}},"respElementTypes":[{"element_type":{"created_at":"2011-08-10T11:00:19Z","updated_at":"2011-08-10T11:00:19Z","id":1,"tag_name":"head"}},
There are four tags in the JSON:
page
website
elementType
elementData
I would like to create four arrays and populate them with the object that matches these tags.
I would image the code is something like this:
#Get the json from repo using net/http
uri = URI.parse("http://127.0.0.1:3007/repository/infoid/1.json")
http = Net::HTTP.new(uri.host, uri.port)
response = http.request(Net::HTTP::Get.new(uri.request_uri))
#x = response.to_hash
#pages = Array.new
#websites= Array.new
#elementDatas = Array.new
#elementTypes = Array.new
#enter code here`#For every bit of the hash, find out what it is and allocate it accordingly
#x.each_with_index do |e,index|
if e.tagName == pages #Getting real javascripty here. There must be someway to check the tag or title of the element
#pages[index]=e
end
My goal for the returned value is to have four arrays, each containing different types of objects:
#pagesArray[1]
should contain the first occurrence of a page object in the JSON string. Then do the same for the other ones.
Of course I'd need to break down the object further but once I can break down the top level and categorize them, then I can go deeper.
In the JSON there are already tag titles respPages and respWebsites which group all the objects.
How do I turn JSON back into objects in Ruby and reference them using something like the tag name?
You should be able to decode anything in JSON format using the standard JSON library:
JSON.load(...)
It will throw exceptions on malformed JSON data, so be sure to test it thoroughly and make sure it can handle all the important cases.
If you're trying to navigate the structure of the JSON itself, you probably need to write a series of recursive methods that handle each case along the way. A good pattern to start with is this:
#data.each do |key, value|
case (key)
when 'someKey'
handle_some_key(value)
when 'otherKey'
handle_other_key(value)
end
end
You can either break out the behavior into methods as in this example, or inline it if the logic is fairly straightforward.
As a note, an alternative to Array.new is simply [ ] as it is in JavaScript. For example:
#pages = [ ]
You'll see this used frequently in most Ruby examples. The alternative to Hash.new is { }.
The following works:
json = {"respPages"=>[{"page"=>{"page_url"=>"http://www.google.com", "created_at"=>"2011-08-10T11:00:19Z", "website_id"=>1, "updated_at"=>"2011-08-10T11:00:19Z", "id"=>1}}, {"page"=>{"page_url"=>"http://www.blank.com/services/content_services/", "created_at"=>"2011-08-10T11:02:46Z", "website_id"=>1, "updated_at"=>"2011-08-10T11:02:46Z", "id"=>2}}],
"respSite"=>{"website"=>{"created_at"=>"2011-08-10T11:00:19Z", "website_id"=>nil, "updated_at"=>"2011-08-10T11:00:19Z", "website_url"=>nil, "id"=>1}},
"respElementTypes"=>[{"element_type"=>{"created_at"=>"2011-08-10T11:00:19Z", "updated_at"=>"2011-08-10T11:00:19Z", "id"=>1, "tag_name"=>"head"}}]}
#respPages, #respSite, #respElementTypes = [], [], []
json.each do |key_category, group_category|
group_category.each do |hash|
if group_category.is_a? Array
eval("##{key_category}") << hash.values.first
elsif group_category.is_a? Hash
eval("##{key_category}") << hash[1]
end
end
end
there weren't any respData in your sample but you've got the idea.
I have an array of hashes. Each entry looks like this:
- !map:Hashie::Mash
name: Connor H Peters
id: "506253404"
I'm trying to create a second array, which contains just the id values.
["506253404"]
This is how I'm doing it
second_array = first_array.map { |hash| hash[:id] }
But I'm getting this error
TypeError in PagesController#home
can't convert Symbol into Integer
If I try
second_array = first_array.map { |hash| hash["id"] }
I get
TypeError in PagesController#home
can't convert String into Integer
What am I doing wrong? Thanks for reading.
You're using Hashie, which isn't the same as Hash from ruby core. Looking at the Hashie github repo, it seems that you can access hash keys as methods:
first_array.map { |hash| hash.id }
Try this out and see if that works--make sure that it doesn't return the object_id. As such, you may want to double-check by doing first_array.map { |hash| hash.name } to see if you're really accessing the right data.
Then, provided it's correct, you can use a proc to get the id (but with a bit more brevity):
first_array.map(&:id)
This sounds like inside the map block that hash is not actually a hashie - it's an array for some reason.
The result is that the [] method is actually an array accessor method and requires an integer. Eg. hash[0] would be valid, but not hash["id"].
You could try:
first_array.flatten.map{|hash| hash.id}
which would ensure that if you do have any nested arrays that nesting is removed.
Or perhaps
first_array.map{|hash| hash.id if hash.respond_to?(:id)}
But either way you may end up with unexpected behaviour.