I have two different types of codes.
AllCodes = [
{
group_name: 'Marked',
group_codes: [
{
code: '1A',
description: 'Marked.'
}
],
.. //next group codes.
}
]
AllCodes = [
{
code: '2',
description: 'Located. Facilities Marked.'
}
.. //next codes.
]
I need to form an array of this format.
[
{
code: '1A',
description: 'example'
},
.. // next code
]
I did it this way, but I do not really like this approach, how can I dry up the code?
def up
Account.all.each do |account|
arrayed_codes = []
account.one_call_center.response_codes_repository_class::AllCodes.collect do |codes|
if response_code[:group_codes]
response_code[:group_codes].each do |group_codes|
arrayed_codes << {
code: group_codes[:code],
description: group_codes[:description]
}
end
else
arrayed_codes << {
code: response_code[:code],
description: response_code[:description]
}
end
end
arrayed_codes.each do |res_code|
ResponseCode.create!(account: account,
one_call_center_id: account.one_call_center.id,
close_code: res_code[:code],
description: res_code[:description],
ticket_types: account.one_call_center.ticket_types.keys)
end
end
end
# obj is an each element of your AllCodes array
codes = AllCodes.inject([]) do |codes_array, obj|
if obj.has_key?(:group_codes)
codes_array += obj[:group_codes]
else
codes_array << obj
end
end
codes_array is an injected array. Iterating over your AllCodes, if current object has_key?(:group_codes), we should take obj[:group_codes] (because it's already an array of needed format), so we merge our codes_array with it: codes_array += obj[:group_codes]. If it doesn't have that key, than it's already hash of needed format. So we just add this element to the array.
Related
I have an array of strings of this format ['config = 3', 'config_b.root.a.b.c = 13'] ;
my goal is to create the following json object from them
{
"config": 3,
"config_b": {
"root": {
"a": {
"b": {
"c": 13
}
}
}
}
}
this is my current working approach
# inputs is an array of strings
def create_config(inputs)
hash={}
inputs.each do |x|
value = x.split("=")[1]
keys = x.split("=")[0].strip.split(".")
add_item(keys,value,hash)
end
print hash
end
# recusive function for adding items
def add_item(keys,value,hash)
current = keys.shift
if keys.empty?
hash[current] = value
else
hash[current] = {}
add_item(keys,value,hash[current])
end
end
I would like to know if anyone has a better approach for solving this, Thanks
I think I have a solution.
def create_config(inputs)
inputs.map do |e|
keys, value = e.split(' = ')
keys.split('.').reverse.inject(value) { |assigned_value, key| { key => assigned_value } }
end.reduce(:merge)
end
I tried it with
['config = 3', 'config_b.root.a.b.c = 13']
and got
{"config"=>"3", "config_b"=>{"root"=>{"a"=>{"b"=>{"c"=>"13"}}}}}
I'd like to understand how to understand the way to apply transformation (underscore in my case) on each key when parsing a JSON file with Oj.
For example in ruby/rails :
require 'oj'
Oj.optimize_rails()
def transform_keys(hash)
hash.deep_transform_keys { |k| k.to_s.underscore }
end
data = '{ "JsonKey": "JsonValue", "JsonKey2": { "JsonSubKey2": "JsonSubValue" }, "Array": ["ValueArray1","ValueArray2"] }'
data2 = '[{ "JsonKey": "JsonValue", "JsonKey2": { "JsonSubKey2": "JsonSubValue" }, "Array": ["ValueArray1","ValueArray2"] }]'
json_data = JSON.parse(data)
case json_data
when Hash
transform_keys(json_data)
when Array
json_data.map { |hash| transform_keys(hash) }
end
This do the job but it iterate twice : one during parsing and for apply the transformation.
Checking oj, I found :
::Oj::ScHandler http://www.ohler.com/oj/doc/Oj/ScHandler.html
::Oj::Saj http://www.ohler.com/oj/doc/Oj/Saj.html
Let's try :
class UnderscoreKeyHandler < ::Oj::ScHandler
def hash_start
{}
end
def hash_set(h,k,v)
h[k.to_s.underscore] = v
end
def array_start
[]
end
def array_append(a,v)
a << v
end
def add_value(v)
end
end
handler = UnderscoreKeyHandler.new
Oj.sc_parse(handler, data)
It work! Ok, let's benchmark this :
require 'oj'
Oj.optimize_rails()
hash = Hash.new
key = 'abcd'
1000.times { hash[key.succ!] = hash.keys }
json = hash.to_json; nil
def transform_keys(hash)
hash.deep_transform_keys { |k| k.to_s.underscore }
end
10000 * Benchmark.realtime {
json_data = JSON.parse(json)
case json_data
when Hash
transform_keys(json_data)
when Array
json_data.map { |hash| transform_keys(hash) }
end
} # Between 2400 and 3500
10000 * Benchmark.realtime { Oj.sc_parse(handler, json) } # Between 700 and 1600
Perfect? I seem to be but i feel insecure...
In fact i just want to override each key for a hash and not rewrite a full handler. Is there a way?
Moreover :
What is the difference between ::Oj::ScHandler and ::Oj::Saj ?
What are the risk of using a custom handler
What is the purpose of the add_value method.
How can i raise another error than the Oj::ParseError default error?
I have a JSON string:
{
"normal_domains":[{
"urls [
"domain1.com",
"domain2.com"
],
"id":3,
"find":"ama",
"type":"text"
}
],
"premium_domains":[{
"urls":[
"domain3.com",
"domain4.com"
],
"id":1,
"find":"amg",
"type":"text"
}
]
}
I want to output a list for each domain in the hash with corresponding attributes:
Domain type: normal_domains
Domain: domain3.com
ID: 3
Find: ama
-- for each domain --
The code I have is this, but I cannot get it working. It returns NoMethodError: undefined method [] for nil:NilClass:
from_api = '{"normal_domains":[{"urls":["domain1.com","domain2.com"],"id":3,"find":"ama","type":"text"}],"premium_domains":[{"urls":["domain3.com","domain4.com"],"id":1,"find":"amg","type":"text"}]}'
result = JSON.parse from_api
result.each do |child|
loop_index = 0
child.each do |sub_child|
puts "Domain type: #{child}"
puts "Domain: #{sub_child[loop_index]['urls']}"
puts "ID: #{sub_child[loop_index]['id']}"
puts "Find: #{sub_child[loop_index]['find']}"
loop_index += 1
end
end
The hash returned from JSON.parse does not have a .each method.
Imagine your input hash in a more organized way:
{
"normal_domains":[ {
"urls [
"domain1.com",
"domain2.com"
],
"id":3,
"find":"ama",
"type":"text"
}],
"premium_domains":[{
"urls":[
"domain3.com",
"domain4.com"
],
"id":1,
"find":"amg",
"type":"text"
}]
}
You code should be:
result = JSON.parse from_api
result.keys.each do |domain_type|
childArray = result[domain_type]
childArray.each do |child|
urls = child["urls"]
urls.each do |url|
puts "Domain type: #{domain_type}"
puts "Domain: #{url}"
puts "ID: #{child['id']}"
puts "Find: #{child['find']}"
end
end
end
If you want to iterate over an array in a style that is common in C i.e. using array indexes, you should do it like
urls = domain['urls']
(0..urls.length).each do |i|
puts " URL: #{urls[i]}"
end
or at least handle the case when indexing an array element returns nil when the code tries to access data beyond what has been entered in the array. Using array indexes is often unnecessary since iterators can be used.
Using iterators, one does not need the indexes and there is no need to check if the access is beyond the boundaries of a container.
result.each do |key,value|
puts "Domain type: #{key}"
value.each do |domain|
id = domain['id']
find = domain['find']
type = domain['type']
puts " ID: #{id}"
puts " Find: #{find}"
puts " Type: #{type}"
domain['urls'].each do |url|
puts " URL: #{url}"
end
end
end
I seem lost trying to achieve the following, I tried all day please help
I HAVE
h = {
"kv1001"=> {
"impressions"=>{"b"=>0.245, "a"=>0.754},
"visitors" =>{"b"=>0.288, "a"=>0.711},
"ctr" =>{"b"=>0.003, "a"=>0.003},
"inScreen"=>{"b"=>3.95, "a"=>5.031}
},
"kv1002"=> {
"impressions"=>{"c"=>0.930, "d"=>0.035, "a"=>0.004, "b"=>0.019,"e"=>0.010},
"visitors"=>{"c"=>0.905, "d"=>0.048, "a"=>0.005, "b"=>0.026, "e"=>0.013},
"ctr"=>{"c"=>0.003, "d"=>0.006, "a"=>0.004, "b"=>0.003, "e"=>0.005},
"inScreen"=>{"c"=>4.731, "d"=>4.691, "a"=>5.533, "b"=>6.025, "e"=>5.546}
}
}
MY GOAL
{
"segment"=>"kv1001=a",
"impressions"=>"0.754",
"visitors"=>"0.711",
"inScreen"=>"5.031",
"ctr"=>"0.003"
}, {
"segment"=>"kv1001=b",
"impressions"=>"0.245",
"visitors"=>"0.288",
"inScreen"=>"3.95",
"ctr"=>"0.003"
}, {
"segment"=>"kv1002=a",
"impressions"=>"0.004"
#... etc
}
My goal is to create a hash with 'kv1001=a' i.e the letters inside the hash and assign the keys like impressions, visitors etc. The example MY GOAL has the format
So format type "kv1001=a" must be constructed from the hash itself, a is the letter inside the hash.
I have solved this now
`data_final = []
h.each do |group,val|
a = Array.new(26){{}}
val.values.each_with_index do |v, i|
keys = val.keys
segment_count = v.keys.length
(0..segment_count-1).each do |n|
a0 = {"segment" => "#{group}=#{v.to_a[n][0]}", keys[i] => v.to_a[n][1]}
a[n].merge! a0
if a[n].count > 4
data_final << a[n]
end
end
end
end`
Here's a simpler version
h.flat_map do |segment, attrs|
letters = attrs.values.flat_map(&:keys).uniq
# create a segment entry for each unique letter
letters.map do |letter|
seg = {"segment" => "#{segment}=#{letter}"}
seg.merge Hash[attrs.keys.map {|key| [key,attrs[key][letter]]}]
end
end
Output:
[{"segment"=>"kv1001=b",
"impressions"=>0.245,
"visitors"=>0.288,
"ctr"=>0.003,
"inScreen"=>3.95},
{"segment"=>"kv1001=a",
"impressions"=>0.754,
"visitors"=>0.711,
"ctr"=>0.003,
"inScreen"=>5.031},
{"segment"=>"kv1002=c",
"impressions"=>0.93,
"visitors"=>0.905,
"ctr"=>0.003,
"inScreen"=>4.731},
{"segment"=>"kv1002=d",
"impressions"=>0.035,
"visitors"=>0.048,
"ctr"=>0.006,
"inScreen"=>4.691},
{"segment"=>"kv1002=a",
"impressions"=>0.004,
"visitors"=>0.005,
"ctr"=>0.004,
"inScreen"=>5.533},
{"segment"=>"kv1002=b",
"impressions"=>0.019,
"visitors"=>0.026,
"ctr"=>0.003,
"inScreen"=>6.025},
{"segment"=>"kv1002=e",
"impressions"=>0.01,
"visitors"=>0.013,
"ctr"=>0.005,
"inScreen"=>5.546}]
def self.return_this_data_for_map_method
data = { :labels => [], datasets: [data: []] }
dictionary = {}
results.each do |teams|
team = teams[0]
teamMembers = teams[1]
if dictionary[team].nil?
dictionary[team] = teamMembers
else
dictionary[team] += teamMembers
end
end
data[:labels] << dictionary.keys
data[:datasets][0][:data] << dictionary.values
data
end
This is the data I am getting out
=> {:labels=>[["CUBS", "CARDS", "ROCKIES", "ASTROS"]]:datasets=>[{:data=>[[72, 93, 74, 28]]}]}
This is how I am trying to get my data
=> {:labels=>["CUBS", "CARDS", "ROCKIES", "ASTROS"], :datasets=>[{:data=>[72, 93, 74, 28]}]}
It's wrapping it like it's still in an Array of Arrays, I am not quite seeing how to break how of it. Any suggestions on how I can fix my code would be appreciated.
using ruby 2.3.1
The simplest solution is to flatten the arrays prior to returning the data:
data = { :labels => [], datasets: [data: []] }
dictionary = {}
results.each do |teams|
team = teams[0]
teamMembers = teams[1]
if dictionary[team].nil?
dictionary[team] = teamMembers
else
dictionary[team] += teamMembers
end
end
data[:labels] << dictionary.keys
data[:datasets][0][:data] << dictionary.values
data[:labels].flatten!
data[:datasets][0][:data].flatten!
data
#maxpleaner and #rails_id were correct
def self.return_this_data_for_map_method
data = { :labels => [], datasets: [data: []] }
dictionary = {}
results.each do |teams|
team = teams[0]
teamMembers = teams[1]
if dictionary[team].nil?
dictionary[team] = teamMembers
else
dictionary[team] += teamMembers
end
end
data[:labels] += dictionary.keys
data[:datasets][0][:data] += dictionary.values
data
end