How I get the substring? - ruby-on-rails

how can I get the substring of the string:
[{
"ProductId"=>198,
"AttributesXml"=>"<Attributes><ProductAttribute ID=\"590\"><ProductAttributeValue><Value>1691</Value></ProductAttributeValue></ProductAttribute></Attributes>",
"StockQuantity"=>1,
"AllowOutOfStockOrders"=>false,
"Sku"=>nil,
"ManufacturerPartNumber"=>nil,
"Gtin"=>nil,
"OverriddenPrice"=>nil,
"NotifyAdminForQuantityBelow"=>1, "Id"=>1756
},
I want only the value of "ProductAtrribute ID": 590. Only the number.
I have checked out the ruby docs, but I cannot find a solution.

The tag has rails, if you are in rails then you can leverage nokogiri gem to handle parsing the AttributesXml section:
require 'nokogiri'
array = [{"ProductId"=>198, "AttributesXml"=>"<Attributes><ProductAttribute
ID=\"590\"><ProductAttributeValue><Value>1691</Value>
</ProductAttributeValue></ProductAttribute></Attributes>",
"StockQuantity"=>1, "AllowOutOfStockOrders"=>false,"Sku"=>nil,
"ManufacturerPartNumber"=>nil, "Gtin"=>nil, "OverriddenPrice"=>nil,
"NotifyAdminForQuantityBelow"=>1, "Id"=>1756}]
array.each do |item|
doc = Nokogiri::XML(item['AttributesXml'])
puts doc.xpath("//ProductAttribute").attribute('ID')
end
=> 590

I'd use:
require 'nokogiri'
foo = [
{
"ProductId"=>198,
"AttributesXml"=>"<Attributes><ProductAttribute ID=\"590\"><ProductAttributeValue><Value>1691</Value></ProductAttributeValue></ProductAttribute></Attributes>",
"StockQuantity"=>1,
"AllowOutOfStockOrders"=>false,
"Sku"=>nil,
"ManufacturerPartNumber"=>nil,
"Gtin"=>nil,
"OverriddenPrice"=>nil,
"NotifyAdminForQuantityBelow"=>1, "Id"=>1756
},
]
foo.map { |hash|
Nokogiri::XML(hash['AttributesXml']).at('ProductAttribute')['ID']
}
# => ["590"]
It'll return the ID from all <ProductAttribute> nodes in all the hashes in the array.

You can make use of scan
array = [{"ProductId"=>198, "AttributesXml"=>"<Attributes>
<ProductAttribute
ID=\"590\"><ProductAttributeValue><Value>1691</Value>
</ProductAttributeValue></ProductAttribute></Attributes>",
"StockQuantity"=>1, "AllowOutOfStockOrders"=>false,""Sku"=>nil,
"ManufacturerPartNumber"=>nil, "Gtin"=>nil, "OverriddenPrice"=>nil,
"NotifyAdminForQuantityBelow"=>1, "Id"=>1756},.. ]
and
array.each do |hash|
hash["AttributesXml"].scan(/ProductAttribute\s+ID=\"(\d+)\"/).flatten
#=> ["590"]
# This will return matched values
end

Related

ruby add strings from array

I am building a simple breadcrumb in ruby but I am not sure how to really implement my logic.
Let's say I have an array of words that are taken from my request.path.split("/) ["", "products", "women", "dresses"]
I want to push the strings into another array so then in the end I have ["/", "/products", "products/women", "products/women/dresses"] and I will use it as my breadcrumb solution.
I am not good at ruby but for now I came up with following
cur_path = request.path.split('/')
cur_path.each do |link|
arr = []
final_link = '/'+ link
if cur_path.find_index(link) > 1
# add all the previous array items with the exception of the index 0
else
arr.push(final_link)
end
end
The results should be ["/", "/products", "/products/women", "/products/women/dresses"]
Ruby's Pathname has some string-based path manipulation utilities, e.g. ascend:
require 'pathname'
Pathname.new('/products/women/dresses').ascend.map(&:to_s).reverse
#=> ["/", "/products", "/products/women", "/products/women/dresses"]
This is my simplest solution:
a = '/products/women/dresses'.split('/')
a.each_with_index.map { |e,i| e.empty? ? '/' : a[0..i].join('/') }
Using map and with_index it can be done like this:
arr = ["", "products", "women", "dresses"]
arr.map.with_index { |item, index| "#{arr[0...index].join('/')}/#{item}" }
This is another option using Enumerable#each_with_object and Enumerable#each_with_index:
ary = '/products/women/dresses'.split('/')
ary[1..].map
.with_index
.with_object([]) { |(folder, idx), path| path << [path[idx-1], folder].join('/') }.unshift('/')
Or also:
(ary.size - 1).times.map { |i| ary.first(i + 2).join('/') }.unshift('/')

How to do get and set values in Ruby Hashes of a given nested key?

I tried something like this with vine gem but not working. Is there any other smart way
Note i do not want to use complex Hash Class like we have here
require 'vine'
require 'json'
json = '{
"name":"John",
"address":{
"street":"street 1",
"country":"country1"
},
"phone_numbers":[
{
"type":"mobile",
"number":"234234"
},
{
"type":"fixed",
"number":"2342323423"
}
]
}'
h = JSON.parse(json)
{"name"=>"John", "address"=>{"street"=>"street 1", "country"=>"country1"}, "phone_numbers"=>[{"type"=>"mobile", "number"=>"234234"}, {"type"=>"fixed", "number"=>"2342323423"}]}
a = h.access("phone_numbers.0.type")
mobile
b = h.set("phone_numbers.0.type", "tablet")
{"name"=>"John", "address"=>{"street"=>"street 1", "country"=>"country1"}, "phone_numbers"=>{0=>{:type=>"tablet"}}}
expected result is
{"name"=>"John", "address"=>{"street"=>"street 1", "country"=>"country1"}, "phone_numbers"=>[{"type"=>"tablet", "number"=>"234234"}, {"type"=>"fixed", "number"=>"2342323423"}]}
It is not working for array, or i am missing something
Thanks
Try the below:
require 'json'
#considering that the json is same as you mentioned in the question
h = JSON.parse(json)
# For accessing name
p h['name']
#for changing the name
h['name'] = 'ABC'
# for getting address
p h['address']['street']
# for setting address
h['address']['street'] = "my street"
#get phone number
h['phone_numbers'].each do |ph|
p ph['type']
#Set mobile number
ph['number'] = "123"
end
In the above you received a single object in json.
But in case if you receive multiple objects, then parse them like below
json_array = JSON.parse(json)
json_array.each do |h|
#All the above steps will remain same
end

Rails join array inside map without escaping

I have an array of hashes that I map into a string
Example:
array_of_hashes = [{
:me => 'happy',
:you => 'notsohappy',
:email => [
{"Contact"=>"", "isVerified"=>"1"},
{"Contact"=>"me#example.com", "isVerified"=>"1"},
{"Contact"=>"you#example.com", "isVerified"=>"1"}
]
},{another instance here...}]
Now I want to convert this to a new array that will give me:
["happy", "notsodhappy", "me#example.com", "you#example.com"]
I need to map and reject empty email addresses in the "email" array of hashes.
So far I tried:
array_of_hashes.map{|record| [
record['me'],
record['you'],
record['email'].map { |email| email['Contact']}.reject { |c| c.empty? }.join('", "')
] }
But this returns ["happy", "notsohappy", "me#example.com\", \"you#example.com"]
The quotes are escapes even if I add .html_safe after the .join
In short, it's insisting to keep the joined array a single string. I need it split to separate strings... as many as are in the array.
I need to get rid of these quotes because I am trying to export the array as CSV and so far it's not splitting the email addresses to separate columns.
Suggestions?
array_of_hashes.map do |h|
[h[:me], h[:you]].push(
h[:email].map {|e|e["Contact"]}.reject(&:empty?)
).flatten
end
# => [["happy", "notsohappy", "me#example.com", "you#example.com"], ...]
results = []
array_of_hashes.each do |hash|
single_result = []
single_result << hash[:me]
single_result << hash[:you]
hash[:email].each do |email|
single_result << email["Contact"] if email["Contact"].present?
end
results << single_result
return results
end
This will results : -
2.3.1 :091 > results
=> [["happy", "notsohappy", "me#example.com", "you#example.com"], ["happy", "notsohappy", "me#example.com", "you#example.com"], ["happy", "notsohappy", "me#example.com", "you#example.com"]]

Ruby way to loop and check subsequent values against each other

I have an array that contains dates and values. An example of how it might look:
[
{'1/1/2010' => 'aa'},
{'1/1/2010' => 'bb'},
{'1/2/2010' => 'cc'},
{'1/2/2010' => 'dd'},
{'1/3/2010' => 'ee'}
]
Notice that some of the dates repeat. I'm trying to output this in a table format and I only want to show unique dates. So I loop through it with the following code to get my desired output.
prev_date = nil
#reading_schedule.reading_plans.each do |plan|
use_date = nil
if plan.assigned_date != prev_date
use_date = plan.assigned_date
end
prev_date = plan.assigned_date
plan.assigned_date = use_date
end
The resulting table will then look something like this
1/1/2010 aa
bb
1/2/2010 cc
dd
1/3/2010 ee
This work fine but I am new to ruby and was wondering if there was a better way to do this.
Enumerable.group_by is a good starting point:
require 'pp'
asdf = [
{'1/1/2010' => 'aa'},
{'1/1/2010' => 'bb'},
{'1/2/2010' => 'cc'},
{'1/2/2010' => 'dd'},
{'1/3/2010' => 'ee'}
]
pp asdf.group_by { |n| n.keys.first }.map{ |a,b| { a => b.map { |c| c.to_a.last.last } } }
# >> [{"1/1/2010"=>["aa", "bb"]}, {"1/2/2010"=>["cc", "dd"]}, {"1/3/2010"=>["ee"]}]
Which should be a data structure you can bend to your will.
I don't know as though it's better, but you could group the values by date using (e.g.) Enumerable#reduce (requires Ruby >= 1.8.7; before that, you have Enumerable#inject).
arr.reduce({}) { |memo, obj|
obj.each_pair { |key, value|
memo[key] = [] if ! memo.has_key?(key);
memo[key] << value
}
memo
}.sort
=> [["1/1/2010", ["aa", "bb"]], ["1/2/2010", ["cc", "dd"]], ["1/3/2010", ["ee"]]]
You could also use Array#each to similar effect.
This is totally a job for a hash.
Create a hash and use the date as the hashkey and an empty array as the hashvalue.
Then accumulate the values from the original array in the hashvalue array

what is the best way to convert a json formatted key value pair to ruby hash with symbol as key?

I am wondering what is the best way to convert a json formatted key value pair to ruby hash with symbol as key:
example:
{ 'user': { 'name': 'foo', 'age': 40, 'location': { 'city' : 'bar', 'state': 'ca' } } }
==>
{ :user=>{ :name => 'foo', :age =>'40', :location=>{ :city => 'bar', :state=>'ca' } } }
Is there a helper method can do this?
using the json gem when parsing the json string you can pass in the symbolize_names option. See here: http://flori.github.com/json/doc/index.html (look under parse)
eg:
>> s ="{\"akey\":\"one\",\"bkey\":\"two\"}"
>> JSON.parse(s,:symbolize_names => true)
=> {:akey=>"one", :bkey=>"two"}
Leventix, thank you for your answer.
The Marshal.load(Marshal.dump(h)) method probably has the most integrity of the various methods because it preserves the original key types recursively.
This is important in case you have a nested hash with a mix of string and symbol keys and you want to preserve that mix upon decode (for instance, this could happen if your hash contains your own custom objects in addition to highly complex/nested third-party objects whose keys you cannot manipulate/convert for whatever reason, like a project time constraint).
E.g.:
h = {
:youtube => {
:search => 'daffy', # nested symbol key
'history' => ['goofy', 'mickey'] # nested string key
}
}
Method 1: JSON.parse - symbolizes all keys recursively => Does not preserve original mix
JSON.parse( h.to_json, {:symbolize_names => true} )
=> { :youtube => { :search=> "daffy", :history => ["goofy", "mickey"] } }
Method 2: ActiveSupport::JSON.decode - symbolizes top-level keys only => Does not preserve original mix
ActiveSupport::JSON.decode( ActiveSupport::JSON.encode(h) ).symbolize_keys
=> { :youtube => { "search" => "daffy", "history" => ["goofy", "mickey"] } }
Method 3: Marshal.load - preserves original string/symbol mix in the nested keys. PERFECT!
Marshal.load( Marshal.dump(h) )
=> { :youtube => { :search => "daffy", "history" => ["goofy", "mickey"] } }
Unless there is a drawback that I'm unaware of, I'd think Method 3 is the way to go.
Cheers
There isn't anything built in to do the trick, but it's not too hard to write the code to do it using the JSON gem. There is a symbolize_keys method built into Rails if you're using that, but that doesn't symbolize keys recursively like you need.
require 'json'
def json_to_sym_hash(json)
json.gsub!('\'', '"')
parsed = JSON.parse(json)
symbolize_keys(parsed)
end
def symbolize_keys(hash)
hash.inject({}){|new_hash, key_value|
key, value = key_value
value = symbolize_keys(value) if value.is_a?(Hash)
new_hash[key.to_sym] = value
new_hash
}
end
As Leventix said, the JSON gem only handles double quoted strings (which is technically correct - JSON should be formatted with double quotes). This bit of code will clean that up before trying to parse it.
Recursive method:
require 'json'
def JSON.parse(source, opts = {})
r = JSON.parser.new(source, opts).parse
r = keys_to_symbol(r) if opts[:symbolize_names]
return r
end
def keys_to_symbol(h)
new_hash = {}
h.each do |k,v|
if v.class == String || v.class == Fixnum || v.class == Float
new_hash[k.to_sym] = v
elsif v.class == Hash
new_hash[k.to_sym] = keys_to_symbol(v)
elsif v.class == Array
new_hash[k.to_sym] = keys_to_symbol_array(v)
else
raise ArgumentError, "Type not supported: #{v.class}"
end
end
return new_hash
end
def keys_to_symbol_array(array)
new_array = []
array.each do |i|
if i.class == Hash
new_array << keys_to_symbol(i)
elsif i.class == Array
new_array << keys_to_symbol_array(i)
else
new_array << i
end
end
return new_array
end
Of course, there is a json gem, but that handles only double quotes.
Another way to handle this is to use YAML serialization/deserialization, which also preserves the format of the key:
YAML.load({test: {'test' => { ':test' => 5}}}.to_yaml)
=> {:test=>{"test"=>{":test"=>5}}}
Benefit of this approach it seems like a format that is better suited for REST services...
The most convenient way is by using the nice_hash gem: https://github.com/MarioRuiz/nice_hash
require 'nice_hash'
my_str = "{ 'user': { 'name': 'foo', 'age': 40, 'location': { 'city' : 'bar', 'state': 'ca' } } }"
# on my_hash will have the json as a hash
my_hash = my_str.json
# or you can filter and get what you want
vals = my_str.json(:age, :city)
# even you can access the keys like this:
puts my_hash._user._location._city
puts my_hash.user.location.city
puts my_hash[:user][:location][:city]
If you think you might need both string and symbol keys:
JSON.parse(json_string).with_indifferent_access

Resources