How to iterate through array of strings from database Ruby on Rails - ruby-on-rails

I save an array of strings to my rails database, but when I go to use it in the view, I believe it is printing the string definition of the array. Am I dealing with JSON here? (aka when it saves to the database is it just an array wrapped in a string?)
How do I have it so that in my view, it simply displays the items?
<%= record.items %>
displays inside my html tag:
["item1", "item2", "item3"]
I tried iterating through record.items.each do |item| but that did not work.

If you're saving an "exact" array as a String, then Array#each won't work, because isn't a method in the String class.
Maybe isn't the best option, but you could use JSON.parse and this way get your array and be able to iterate over each object inside:
require 'json'
str = '["item1", "item2", "item3"]'
JSON.parse(str).each { |item| p item }
# "item1"
# "item2"
# "item3"
In order this work your string must be an array, in your example the second item is missing its double quote.
You could consider working with serialization or array data types depending on you current database.

A better approach to your issue is to serialize your items column. I think by default it's Array but you can use Hash or JSON.
class Record < ActiveRecord::Base
serialize :items
end
Calling record.items returns the data exactly the way you need. If you go with this you'll have to update your old records to support it.

Related

How to access Chewy results with the dot notation?

I'm using Toptal's Chewy gem to connect and query my Elasticsearch, just like an ODM.
I'm using Chewy along with Elasticsearch 6, Ruby on Rails 5.2 and Active Record.
I've defined my index just like this:
class OrdersIndex < Chewy::Index
define_type Order.includes(:customer) do
field :id, type: "keyword"
field :customer do
field :id, type: "keyword"
field :name, type: "text"
field :email, type: "keyword"
end
end
end
And my model:
class Order < ApplicationRecord
belongs_to :customer
end
The problem here is that when I perform any query using Chewy, the customer data gets deserialized as a hash instead of an Object, and I can't use the dot notation to access the nested data.
results = OrdersIndex.query(query_string: { query: "test" })
results.first.id
# => "594d8e8b2cc640bb78bd115ae644637a1cc84dd460be6f69"
results.first.customer.name
# => NoMethodError: undefined method `name' for #<Hash:0x000000000931d928>
results.first.customer["name"]
# => "Frederique Schaefer"
How can I access the nested association using the dot notation (result.customer.name)? Or to deserialize the nested data inside an Object such as a Struct, that allows me to use the dot notation?
try to use
results = OrdersIndex.query(query_string: { query: "test" }).objects
It converts query result into active record Objects. so dot notation should work. If you want to load any extra association with the above result you can use .load method on Index.
If you want to convert existing ES nested object to accessible with dot notation try to reference this answer. Open Struct is best way to get things done in ruby.
Unable to use dot syntax for ruby hash
also, this one can help too
see this link if you need openStruct to work for nested object
Converting the just-deserialized results to JSON string and deserializing it again with OpenStruct as an object_class can be a bad idea and has a great CPU cost.
I've solved it differently, using recursion and the Ruby's native Struct, preserving the laziness of the Chewy gem.
def convert_to_object(keys, values)
schema = Struct.new(*keys.map(&:to_sym))
object = schema.new(*values)
object.each_pair do |key, value|
if value.is_a?(Hash)
object.send("#{key}=", convert_to_object(value.keys, value.values))
end
end
object
end
OrdersIndex.query(query_string: { query: "test" }).lazy.map do |item|
convert_to_object(item.attributes.keys, item.attributes.values)
end
convert_to_object takes an array of keys and another one of values and creates a struct from it. Whenever the class of one of the array of values items is a Hash, then it converts to a struct, recursively, passing the hash keys and values.
To presence the laziness, that is the coolest part of Chewy, I've used Enumerator::Lazy and Enumerator#map. Mapping every value returned by the ES query into the convert_to_object function, makes every entry a complete struct.
The code is very generic and works to every index I've got.

converting ruby hash into json object

So I am iterating through a set of data and building a hash from it:
clean_response = Array.new
response.each_with_index do |h, idx|
clean_response <<
{
:lat => h["location"]["latitude"],
:lg => h["location"]["longitude"],
:place => h["location"]["name"],
#This grabs the entire hash at "location" because we are wanting all of that data
:profile_picture => h["user"]["profile_picture"],
:hash_tags => h["tags"],
:username => h["user"]["username"],
:fullname => h["user"]["full_name"],
:created_time => (Time.at(h["created_time"].to_i)).to_s,
:image => h["images"]["low_resolution"]["url"] # we can replace this with whichever resolution.
}
end
Which return an array of hashes like so:
[{:lat=>40.7486382,
:lg=>-73.9487686,
:place=>"The Cliffs at LIC",
:profile_picture=>"http://scontent.cdninstagram.com/hphotos-xaf1/t51.2885-19/s150x150/12104940_1653775014895036_286845624_a.jpg",
:hash_tags=>["bouldering"],
:username=>"denim_climber",
:fullname=>"DenimClimber",
:created_time=>2015-10-13 22:58:09 -0400,
:image=>"https://scontent.cdninstagram.com/hphotos-xaf1/t51.2885-15/s320x320/e35/11856571_1062082890510188_611068928_n.jpg"},
{:lat=>40.7459602,
:lg=>-73.9574966,
:place=>"SHI",
:profile_picture=>"http://scontent.cdninstagram.com/hphotos-xaf1/t51.2885-19/11348212_1453525204954535_631200718_a.jpg",
:hash_tags=>["cousins", "suchafunmoment", "johnlennonstyle"],
:username=>"xiomirb",
:fullname=>"Xiomi",
:created_time=>2015-10-13 22:57:21 -0400,
:image=>"https://scontent.cdninstagram.com/hphotos-xaf1/t51.2885-15/s320x320/e35/11375290_1688934151392424_2009781937_n.jpg"}]
I'd like to convert this data to json and then serve it to a specific view.
How can I convert this? I tried the .to_json method but it doesn't return a well formatted one since my UI isn't binding to the data.
You can convert a Ruby hash into JSON using to_json:
require 'json'
your_hash.to_json # gives you a JSON object
But, in your case the data is an array of hashes, but NOT a hash. So, your to_json would not work.
I am not quite sure how you want to do this, but one possibility is to loop through the array of hashes, get each hash and convert that to a JSON object using to_json call (like shown above) and build a new array of JSON objects. This way, you can build an array of JSON objects from an array of hashes.
array_of_json = []
# loop through the array of hashes
clean_response.each do |hash|
array_of_json << hash.to_json
end
array_of_json # array of JSON objects
If by "serve it to a specific view" you mean pass it to a .haml or .erb template, you can pass the array of hashes as is. Both haml and erb will allow you to iterate over the array, and even the hash if you want.
If you mean you want to hand a json string to the browser, #to_json should work fine. Other options are jbuilder or oat when you want to refine what is sent, but to_json should "serve" you well!

Nice array from pluck

I have a model and I love the pluck method I can use. If I do this:
#x = AwesomeModel.all.pluck(:column_one, :column_two)
then I get a multidimensional array: #x[][]. With my sad skills, I work with them using the numbers:
#x[0][1]
how can I can use pluck or a similar method to access the array something like this:
#x[0][:column_two]
If you are concerned about the structure of what you get back from the db, you should simply do:
#x = AwesomeModel.all.select(:column_one, :column_two)
Then you'd keep the fast db query advantage + have AwesomeModel instances, but with only column_one and column_two filled
Or if you desire to do it manually:
#x = AwesomeModel.all.pluck(:column_one, :column_two).map do |array|
OpenStruct.new({column_one: array[0], column_two: array[1] }) }
end
Then you can use it like a regular model:
#x[0].column_one
# or even
#x[0][:column_two]
You could do
class ActiveRecord::Base
def self.pluck_hash(*args)
plucked = pluck(*args)
plucked.map {|ary| Hash[args.zip ary]}
end
end
AwesomeModel.all.pluck_hash(:column_one, :column_two)
#=> [{:column_one => 'value', :column_two => 'value}, {...}, ... ]
First of all, don't use .all.pluck, because it returns an array of values, and that makes you loose all the advantages of ActiveRecord::Relation.
Instead use AwsomeModel.method directly, it would create the query but not run it until you need it, AwsomeModel.select(:column_1, :column_2) would create a
select (awesome_models.column_1, awsome_models.column_2)
query, and the result would be an array of ActiveRecord::Relation objects, which are still chainable, and values are still under keys of the column name eg:
AwsomeModel.select(:column_1, :column_2).first.column_1
Instead of
AwesomeModel.all.pluck(:column_1, :column_2).first[0] # or .first.first

Rails how to build a hash from results

I have a table on Db with two fields: group and text
I want to retrieve all records and convert into an hash grouped by the group field, and containing an array of all texts, something like this:
{
'group_1': ['text1','text2',...]
'group_2': ['text1','text2',...]
'group_3': ['text1','text2',...]
}
I acomplish it partially with this
MyModel.all.group_by(&:group)
but it returns an array with all the full AR object, i just want an array of all the text strings
I was trying with map but I can figure out how to do this without using each
Any idea?
Try the following:
MyModel.all.select([:text, :group]).group_by(&:group)
If you wanted to use an iterator (ex: each/map), here is how you would do it:
grouped_models = MyModel.all.group_by(&:group)
grouped_models.map do |group, models|
grouped_models[group] = models.map(&:text)
end
grouped_models
# => { 'group_1' => ['text1', 'text2'], 'group_2' => ['text'] }

Ruby Hash: can't convert String into Integer

I am new to Ruby, and I am having some problems with hashes.
I have XML returned from the YouTube API that I converted into a hash. Here is the hash returned by Hash.from_xml(): http://pastebin.com/9xxE6iXU
I am trying to grab specific elements from the hash for each result, such as the title, link, author, etc. Whenever I try to loop through the hash or grab a specific element, I receive a "can't convert String into Integer" error.
Here is the code I am using for the loop:
#data["feed"]["entry"]["title"].each do |key, value|
"<p>"+key+" "+value+"</p>"
end
I have also tried grabbing specific elements, such as #data["feed"]["entry"]["title"][0].
How do I loop through the hash and grab specific elements out?
That's happening because #data["feed"]["entry"] is array of hashes:
puts #data["feed"]["entry"].class # => Array
Each element-hash inside this array has "id", "category", "title" etc. values.
For grabbing each title try to use following snippet:
#data["feed"]["entry"].each do |entry|
puts entry["title"]
end
# => "TABE test adult basic education"
"WhatCollegesHopeYouWon'tFindOutAboutACTSATTestPrep..."
....

Resources