Parsing a Ruby Array to JSON - ruby

I have some results:
puts result
That look like this output:
Allowed
20863963
1554906
Denied
3607325
0
Quarantined
156240
0
Debug
p results
output
[["Allowed", 20863963, 1554906], ["Denied", 3607325, 0], ["Quarantined", 156194, 0]]
The headers are:
status,hits,page_views
I need to convert this to json. If the results was in standard csv format then it would be straight forward but how would one approach it if the results format looked like above?
Expected output something similar to this:
[{"status":"Allowed","hits":"20863963","page_views":"1554906"},{"status":"Denied","hits":"3607325","page_views":"0"},{"status":"Quarantined","hits":"156240","page_views":"0"}]
Solution
a = result.map{|s| {status: s[0], hits: s[1].to_i, page_views: s[2].to_i} }
puts a.to_json

Look at to_json method.
require 'json'
# => true
h = {a: 1, b: 2,c: 3}
# => {a: 1, b: 2,c: 3}
h.to_json
# => "{\"a\":1,\"b\":2,\"c\":3}"

output = "Allowed
20863963
1554906
Denied
3607325
0
Quarantined
156240
0"
a = output.split("\n").each_slice(3).map{|s| {status: s[0], hits: s[1].to_i, page_views: s[2].to_i} } # => [{:status=>"Allowed", :hits=>20863963, :page_views=>1554906}, {:status=>"Denied", :hits=>3607325, :page_views=>0}, {:status=>"Quarantined", :hits=>156240, :page_views=>0}]
a.to_json # => => "[{\"status\":\"Allowed\",\"hits\":20863963,\"page_views\":1554906},{\"status\":\"Denied\",\"hits\":3607325,\"page_views\":0},{\"status\":\"Quarantined\",\"hits\":156240,\"page_views\":0}]"

You assign your "headers" into the attr_accessor and then tell JSON to parse that symbol. Here's an example:
class Document
attr_accessor :content
def content
metadata[:content] || metadata['content']
end
def self.parse_contents
txt = File.read(path, {mode: 'r:bom|utf-8'})
page = Document.new
page.metadata = JSON.parse(txt)
page.content = page.metadata['content']
return page
end
end
Hope it helps!

Related

How does Ruby functions return a value even when there is nothing to return?

Below code converts the provided key's value in an array of hashes from JSON to hash if it is not nil. This is demonstrated in example 1.
In example 2 the provided key is nil therefore no changes are made to the data. This is the behavior I want. However I can't understand why this is happening. In example 2, the code doesn't hit line if !hash[key].nil? which means the function must return nil however it appears to be returning data_2. In ruby I understand that functions return the last evaluated statement. In example 2 what exactly is the last evaluated statement?
require 'json'
def convert(arr_of_hashes, key)
arr_of_hashes.each do |hash|
if !hash[key].nil?
begin
JSON.parse(hash[key])
rescue JSON::ParserError => e
raise "Bad"
else
hash[key] = JSON.parse(hash[key], {:symbolize_names => true})
end
end
end
end
data_1 = [ { :key_1 => "Apple", :key_2 => "{\"one\":1, \"two\":2}", :key_3 => 200 }, { :key_1 => "Orange" } ]
data_2 = [ { :key_1 => "Apple", :key_2 => nil, :key_3 => 200 }, { :key_1 => "Orange" } ]
# Example 1
p convert(data_1, :key_2)
# [{:key_1=>"Apple", :key_2=>{:one=>1, :two=>2}, :key_3=>200}, {:key_1=>"Orange"}]
# Example 2
p convert(data_2, :key_4)
# [{:key_1=>"Apple", :key_2=>nil, :key_3=>200}, {:key_1=>"Orange"}]
Consider an extremely basic example:
irb(main):003:0> a = [1, 2, 3]
=> [1, 2, 3]
irb(main):004:0> a.each { |x| p x }
1
2
3
=> [1, 2, 3]
irb(main):005:0>
The #each method
is returning the Enumerable object.
If I wrap this in a method, the method returns the last expression, which evaluates to the Enumerable object a.
irb(main):006:0> def foo(a)
irb(main):007:1> a.each { |x| puts x }
irb(main):008:1> end
=> :foo
irb(main):009:0> foo([1, 2, 3])
1
2
3
=> [1, 2, 3]
irb(main):010:0>

Reading a hash from a file on disk [duplicate]

This question already has answers here:
Saving hashes to file on Ruby
(3 answers)
Closed 5 years ago.
Here is a hash that I save to a file to later be read.
my_hash = {-1 => 20, -2 => 30, -3 => 40}
File.open("my_file.txt", "w") { |f| f.write my_hash }
#how it looks opening the text file
{-1 => 20, -2 => 30, -3 => 40}
When I go to read it, is where my problem is. (following code is separate from top)
my_hash = File.foreach("my_file.txt") { |f| print f }
p my_hash
#=> {-1 => 20, -2 => 30, -3 => 40}nil
that nil messes up the rest of my code..not sure how to get rid of if. Just for clarity the rest of the code...
back_up_hash = {-1 => 20}
if my_hash.nil?
my_hash = back_up_hash
end
That little nil always makes my_hash equal to back_up_hash. I need that .nil? just in case the file is doesn't have the hash, otherwise the problem just gets pushed further down.
I also tried to read (slurp?..it's a small file) the file like this....
my_hash = File.read("my_file.txt") { |f| print f }
p my_hash
=> "{-1 => 20, -2 => 30, -3 => 40}"
# not sure how to get it out of string form...and I have searched for it.
You could use the eval method on the string (source)
eval("{-1 => 20, -2 => 30, -3 => 40}")
=> {-1 => 20, -2 => 30, -3 => 40}
If you want to take a file on disk whose contents are {-1 => 20, -2 => 30, -3 => 40} and make a hash from it, you want:
hash_str = File.read('my_file.txt')
my_hash = eval(hash_str) # Treat a string like Ruby code and evaluate it
# or, as a one-liner
my_hash = eval(File.read('my_file.txt'))
What you were doing is reading in the file and printing it to the screen, one line at a time. The 'print' command does not transform the data, and the foreach method does not map the data it yields to your block into any result. This is why you get nil for your my_hash.
As I recommended in a comment, if you have a Ruby object (like a Hash) and you need to save it to disk and load it later, you may want to use the Marshal module (built into Ruby):
$ irb
irb(main):001:0> h = {-1 => 20, -2 => 30, -3 => 40}
#=> {-1=>20, -2=>30, -3=>40}
irb(main):002:0> File.open('test.marshal','wb'){ |f| Marshal.dump(h, f) }
#=> #<File:test.marshal (closed)>
$ irb # later, a new irb session with no knowledge of h
irb(main):001:0> h = File.open('test.marshal'){ |f| Marshal.load(f) }
#=> {-1=>20, -2=>30, -3=>40}
The proper way to save simple data structures to a file is to serialize them. In this particular case, using JSON is probably a good choice:
# save hash to file:
f.write MultiJson.dump(my_hash)
# load it back:
p MultiJson.load(file_contents)
Keep in mind that JSON is only able to serialize simple, built-in data types (strings, numbers, arrays, hashes and the like). You will not be able to serialize and deserialize custom objects this way without some additional work.
If you don't have MultiJson, try it with JSON instead.
I have had success with these 2 simple methods:
def create_json_copy
File.open("db/json_records/stuff.json","w") do |f|
f.write("#{#existing_data.to_json}")
end
end
def read_json_copy
#json = JSON.parse(File.read("db/json_records/stuff.json")).as_json.with_indifferent_access
#json.each do |identifier,record|
existing_record = Something.find_by(some_column: identifier)
if !existing_record
Something.create!(record.except(:id).except(:created_at).except(:updated_at))
end
end
end
note: #existing_data is a Ruby Hash organised as { some_identifier: record_objet, ... } . I call .to_json on it before writing it to file and then when reading I JSON.parse it followed by .as_json, with_indifferent_access isn't really needed here so you can take it off as long as you substitute the symbols inside the excepts.

Looking to write this ruby code in one line

I want to display my hash in a string like this:
The results are a=100, b=200
When I loop through the hash like so:
a = [{:a => 100}, {:b => 200}]
a.each do |x|
x.each do |k,v|
puts "#{k}=#{v}"
end
end
the output returns strings in multiple lines
a=100
b=200
How can I change it to one string?
I was able to get it working with this
puts a.map{|x|x.map{|k,v|"#{k}=#{v}"}}.join(',')
Not sure if its the best solution though.
{:a => 100, :b => 200}.each { |k,v| puts "#{k}=#{v}" }

Convert Ruby hash into array without calling `Hash#to_a`

The Savon gem I am using is giving me back a single object or an array, and I have no way to know which it will be until the SOAP response comes back.
For convenience I would like to have a nil response converted to [], a single response converted to [obj] and an array stay as an array. This can easily be done with Kernel#Array, thus:
> Array nil
=> []
> Array 1
=> [1]
> Array [1,2,3]
=> [1, 2, 3]
However, because Kernel#Array calls to_a, it fails for Hash which overrides to_a:
> Array({a: 1})
=> [[:a, 1]]
> Array([{a: 1}, {b: 2}])
=> [{:a=>1}, {:b=>2}]
On line 2 above I would like to see [{:a=>1}].
If you are using ActiveSupport, you can do the following:
> Array.wrap({a: 1})
=> [{:a, 1}]
> Array.wrap([{a: 1}, {b: 2}])
=> [{:a=>1}, {:b=>2}]
>> [nil].compact.flatten(1)
=> []
>> [1].compact.flatten(1)
=> [1]
>> [{a: 1, b: 2}].compact.flatten(1)
=> [{:a=>1, :b=>2}]
Currently I am able to bypass Hash#to_a with my own straight_to_a method:
def straight_to_a(o)
o.kind_of?(Array) ? o : [o].compact
end
Thus:
> straight_to_a nil
=> []
> straight_to_a 1
=> [1]
> straight_to_a( {a: 1} )
=> [{:a=>1}]
I'm hoping there's an easier way?
Another poster suggested the use of Active Support. If you don't want to add an extra dependency to your project for just one method, here is the source code for Active Support's Array.wrap:
class Array
def self.wrap(object)
if object.nil?
[]
elsif object.respond_to?(:to_ary)
object.to_ary || [object]
else
[object]
end
end
end
You could easily add this code to your own utilities.rb or core_extensions.rb file and include that in your project.
Your solution seems ok, perhaps you can try something based on flatten, like
def straight_to_a *ary
ary.flatten(1)
end

Select nested array in array from an arugment in Ruby

I have an array:
foo = [[51, 05,1.0],[51,979,0.18]]
What I would like to do is take this array and select all nested arrays that have the last value less than 1. So the output from the above would be
result = [[51,979,0.18]]
I have tried:
foo.select { |p| p.last < 1 }
But I get the error:
NoMethodError (undefined method `last'
The array is much larger than just two but I have listed the above as en example. I thought .select would be right, but I can not get it to work.
Your code works for me.
irb(main):007:0> foo = [[51, 05,1.0],[51,979,0.18]]
=> [[51, 5, 1.0], [51, 979, 0.18]]
irb(main):008:0> foo.select { |p| p.last < 1 }
=> [[51, 979, 0.18]]
If you think bad values may exist in your data, it's worth protecting against them:
foo = [ [51, 05,1.0], [51,979,0.18], 4, nil, {:foo => :bar} ]
foo.select do |x|
if (x.respond_to?(:last))
x.last < 1
else
# the warn call evaluates to nil, thus skipping this element
warn("#{x.class} does not respond to method last")
end
end
you were so close!
instead of p.last use p[-1]
so
foo.select{ |p| p[-1] < 1}
what about this ?
foo.select { |p| p.at(-1) < 1 }

Resources