Converting array with .join not saving value - ruby-on-rails

I have a method do_stuff that takes a string as a value. However, an array of two strings is occasionally passed in. In this situation, I need to convert the array to a single string (without commas). So for example, ["hello", "world"] should become "hello world".
So, if value = array, join the two strings, otherwise leave it alone.
The following line I have does what I want, but I am struggling with actually "saving" the value before passing it to the method do_other_stuff.
def do_stuff(value)
value.join("") if value.is_a? Array
do_other_stuff(value)
end
So I think i am close, but what would be the best way to ensure value is manipulated before passing it to do_other_stuff ?

join does not change your object, you're wasting its return value
value = value.join if value.is_a? Array
Note that "" is the default for the join parameter, so I got rid of it

Replace
value.join("") if value.is_a? Array
With
value = value.join("") if value.is_a? Array
Basically you need to reassign result back to value

Use duck typing instead of checking the class:
def do_stuff(value)
do_other_stuff(value.try(:join, '') || value)
end
.try is from ActiveSupport and will return nil if the object does not respond to the method. In plain old ruby you would write this as:
def do_stuff(value)
do_other_stuff(value.respond_to?(:join) ? value.join("") : value)
end

Related

Ruby to_s isn't converting integer to string

I'm trying to convert some values in a hash into a string but the type stays the same.
recommended_stores = []
results['data'].each do |stores_list|
stores_list['stores'].each do |store|
store['id'].to_s
end
recommended_stores << stores_list['stores']
end
Am I missing something here?
the method #to_s just returns the element converted to a string, but does not actually convert the element to a string permanently. instead of using #each, you could use #map, like this.
results['data'].map do |stores_list|
stores_list['stores'].each do |store|
store['id'] = store['id'].to_s
end
end
That would return an array of arrays, if you want it to be just one array you can use #flat_map.
you got everything but you are not storing it, i think assigning the value of hash with the value.to_s would work, can you try as below
recommended_store = []
results['data'].each do |stores_list|
stores_list['stores'].each do |store|
store['id'] = store['id'].to_s
end
recommended_store << stores_list['stores']
end
Note : in your question array declared is "recommended_store" and last line you are pushing elements in to "recommended_stores" hope its just a typo, and not the cause of problem :-)

how do i check that all attributes/values in a class or json object are nil

I have a method that retrieves an elasticsearch query from an API and converts it to an array of JSON objects.
In some cases, the last JSON object is not a nil object, but all the attributes have nil values.
I'd like to avoid mapping over every object, but I need to filter out those JSON objects that have all nil values.
for example:
=> [#<Api::User:0x006546546
#user_id=1,
#height=70,
#age=25>,
#<Api::User:0x006546542
#user_id=nil
#height=nil
#age=nil>]
I want to remove the all-nil object from the array either while it is in JSON format or after it is converted to an array of Api::User objects.
Is a map the only way to check all values of all objects or is there a less resource-intensive method?
I'd lean heavy on methods that Enumerable and Object give you. From Enumerable we can use select to choose only those elements of the array that meet some condition. That condition is that at least one of the instance variables is not nil, and for that we can use any?.
From Object we will use instance_variables to get a collection of instance variables, and we will access the value of those instance variables with instance_variable_get.
It would look something like this:
filtered_array = array.select do |object|
object.instance_variables.any? { |var| object.instance_variable_get(var) }
end
Of course any? is efficient in that it will stop iteration and return true on the very first "truthy" variable. As suggested by SteveTurczyn if you only need to check the last value, it is even quicker.
filtered_array = array[0..-2] if array[-1].instance_variables.none? { |var| object.instance_variable_get(var) }
Someone may come up with a better way, but it seems you don't need to check all values of all objects, as the first value you check that's non-nil means that object is good. I'd do something that checks each value up to the point that a non-nil value is found. It would also help efficiency if you can order tested fields with most likely to have a value first (e.g. user_id )
array.select do |object|
%i(user_id height age).find do |field_name|
object.send(field_name)
end
end
If it's only the last object that needs to be tested, then even easier...
array.pop if %i(user_id height age).find {|field_name| array.last.send(field_name) }
I'm sure there is a cleaner way, but just in case you need to map each object:
def are_attributes_not_nil?(my_object)
my_object.instance_variables.map{ |attr| my_object.send(attr.to_s[1..-1]).nil? }.uniq.include? false
end
returns false if all attributes are nil
My solution involves first implementing a way to convert the objects into hashes before turning them into the JSON itself.
api_user_array #=> [#<Api::User:0x006546546 ... >, #<Api::User:0x006546542 ... >]
api_user_array = api_user_array.map(&:to_h).reject { |hash| hash.values.all?(&:nil?) }
# should result in an array of hashes with at least one value not nil
This assumes that you implement the Api::User#to_h method. This could be something like this:
def attribute_names
%w[user_id height age]
end
def to_h
attribute_names.each_with_object({}) { |attr, hash| hash[attr] = send(attr) }
# assuming all attributes have a getter, otherwise
attribute_names.each_with_object({}) { |attr, hash| hash[attr] = instance_variable_get("##{attr}") }
end

JSON no implicit conversion of String into Integer in Ruby

I'm grabbing a JSON hash from my server and trying to read it in my ruby script. It seems to be returning the wrong value though, unless I'm just loosing my mind:
I'm calling this from a function:
def get_functions(serial_number)
response = HTTParty.get("#{get_host_url}/v1/devices/#{serial_number}.json")
data = response['scan_option']
return data
end
Here is the returned JSON:
{"can_scan"=>true, "can_brute_ssh"=>false, "can_brute_telnet"=>false, "can_brute_voip"=>false, "can_brute_smtp"=>false, "can_brute_pop3"=>false, "can_google_crawl"=>false, "can_scan_external_ip"=>false, "scan_ip_list"=>["10.10.10.1"], "exclude_ip_list"=>[]}
Which is then read into the following code:
data.each do |d|
#can_scan = d['can_scan']
# ....
end
However this is throwing an error:
no implicit conversion of String into Integer
{foo: :bar}.each do |d|
p d
d['meow']
end
# => [:foo, :bar]
#! TypeError: no implicit conversion of String into Integer
Hash#each yields a two element array ([key, value]). You then try to index that array with d["can_scan"], which fails as arrays can only be indexed with integers.
Instead, directly access the value - data['can_scan'].
I you mean that data in your third snippet (where you call data.each) is the hash mentioned just above it, indeed that would be troublesome. Calling each on a hash will itterate over its key, value pairs, giving you an array in the block var d of the data.each (with a [key, value] pair in it).
You might just want to call data['can_scan'].
Note that the return at the end of your method defenition is not needed in Ruby. You can just do:
def get_functions(serial_number)
response = HTTParty.get("#{get_host_url}/v1/devices/#{serial_number}.json")
response["scan_option"]
end

Getting data out of MatchData got as a result from match function

I have an array of custom objects in it. Those objects have a parameter called name which is a concatenation of 2 strings having a delimiter in between. Eg: name could be Some#Data where 'Some' is first string and 'Data' is another and # is a delimiter.
My intention is update the name parameter for all the objects inside the array such that the param would only have 'Data' (i.e. remove 'Some#') and store the objects inside another array after updating. Below is the code:
final_array = array1.select do |object|
object.name = object.name.match(/#(.*?)$/)
end
When I print object.name.match(/#(.*?)$/) this gives me output as:
#<MatchData "#Data" 1:"Data">
Out of this output, how do I get "Data" from this MatchData. I tried object.name.match(/#(.*?)$/)[1] but it didn't work. Or do I need to change my regex?
I would use #each and #gsub methods:
array.each do |object|
object.name = object.name.gsub(/^.+#/, '')
end

Convert Class Object to string in Ruby

I m in a situation where i need to convert an Object to string so that i can check for Invalid characters/HTML in any filed of that object.
Here is my function for spam check
def seems_spam?(str)
flag = str.match(/<.*>/m) || str.match(/http/) || str.match(/href=/)
Rails.logger.info "** was spam #{flag}"
flag
end
This method use a string and look for wrong data but i don't know how to convert an object to string and pass to this method. I tried this
#request = Request
spam = seems_spam?(#request.to_s)
Please guide
Thanks
You could try #request.inspect
That will show fields that are publicly accessible
Edit: So are you trying to validate each field on the object?
If so, you could get a hash of field and value pairs and pass each one to your method.
#request.instance_values.each do |field, val|
if seems_spam? val
# handle spam
end
If you're asking about implementing a to_s method, Eugene has answered it.
You need to create "to_s" method inside your Object class, where you will cycle through all fields of the object and collecting them into one string.
It will look something like this:
def to_s
attributes.each_with_object("") do |attribute, result|
result << "#{attribute[1].to_s} "
end
end
attribute variable is an array with name of the field and value of the field - [id, 1]
Calling #object.to_s will result with a string like "100 555-2342 machete " which you can check for spam.

Resources