Consider the variables:
ctr = ['cobol',nil,nil,'test',nil,'cobol', nil]
h1 = {
0=>{"ABC"=>"10000100126N", "CDE"=>"2013-08-30-}", "TPP"=>"11400000206633458812N", "APD"=> "01531915972", "PRODUCTID"=>"113n", "OPP"=>"201509n", "CTC"=>"C"},
1=>{"ABC"=>"00000039540A", "CDE"=>"0182.22X", "TPP"=>"1234.565N", "APD"=>"12345600", "PRODUCTID"=>"ACHN", "OPP"=>"00000000000119964.1256", "CTC"=>"00000000000211920"}
}
h2 = {'{' => '+0', 'A' => '+1', 'B' => '+2', '}' => '-0', 'N' => '-5'}
The task is to read the ctr
data and where the value is cobol
, we need to apply logic for those values in h1 hash only.
we need to parse the hash h1
and if the last char in hash's value matches with one of the key in hash h2
then replace that value with the corresponding value and prepend symbol to the string.
For example: when we scan hash h1
, for value "10000100126N", as the last char is N and it exists in h2
, then the output should be '-100001001265' where 5 is appended and - is prepended. [Not that the ctr for this is 'cobol']
But if we look at the second value "CDE"=>"2013-08-30-}", since for this key-value pair, the ctr value is not cobol, wee do nothing with the string.
This is what i have done so far:
h1.each do |k,h|
h.update(h) do |*, v|
# puts v
h2.each do |q,p|
if (v[-1] == q)
v.sub!(v[-1], p[-1])
v.sub!(/(.*?)/, p[0] +'\1')
end
end
v
end
end
This code is updating the string as per the requirement, but its running for all the values in h1
, i need to run the code only for the corresponding index where the value in the array ctr
is 'cobol'
First of all a warning when you are matching Hash
positions with Array
indexes. In your example ['cobol',nil,nil,'test',nil,'cobol', nil]
correspond with the keys ["ABC", "CDE", "TPP", "APD", "PRODUCTID", "OPP", "CTC"]
from the inner Hash
of h1
. Keep in mind that a Hash
is not index based but key based. This means, in theory the order of the hash is not maintained. A better way of doing it is by defining a Hash
, like so: {"ABC"=>"cobol", "CDE"=>nil, "TPP"=>nil, "APD"=>"test", "PRODUCTID"=>nil, "OPP"=>"cobol", "CTC"=>nil}
.
With this warning out of the way, let's get to the answer.
What you are looking for is the Enumerable#zip
function, to combine every value with its corresponding value in ctr
.
[:a, :b, :c].zip([1, 2, 3])
#=> [[:a, 1], [:b, 2], [:c, 3]]
First we need to loop through your hash, you're using Hash#each
. Since this is a transformation Enumerable#map
is a better fit. The map
functions results in a array with transformed values. The resulting array can be transformed back into a Hash
with the correct structure.
[[:a, 1], [:b, 2], [:c, 3]].to_h
#=> {:a => 1, :b => 2, :c => 3}
Here's the solution I came up with. It's not the most clean, but it works.
check_logic = lambda do |type, value|
return value unless type == 'cobol'
return value unless h2.has_key?(value[-1])
"#{h2[value[-1]][0]}#{value[0...-1]}#{h2[value[-1]][-1]}"
end
result = h1.map { |k1, v1| [k1, v1.zip(ctr).map { |(k2, v2), type| [k2, check_logic.call(type, v2)] }.to_h] }.to_h
#=> {0=>{"ABC"=>"-100001001265", "CDE"=>"2013-08-30-}", "TPP"=>"11400000206633458812N", "APD"=>"01531915972", "PRODUCTID"=>"113n", "OPP"=>"201509n", "CTC"=>"C"}, 1=>{"ABC"=>"+000000395401", "CDE"=>"0182.22X", "TPP"=>"1234.565N", "APD"=>"12345600", "PRODUCTID"=>"ACHN", "OPP"=>"00000000000119964.1256", "CTC"=>"00000000000211920"}}
As you can see I'm using zip
to combine each value of the Hash
with the ctr
Array
. I'm also using mass assignment (don't know the correct term). A simple example of this is:
(v1, v2, v3) = [1, 2, 3]
Resulting in v1
having value 1
, v2
having value 2
. In the second map
there are 2 params, the first is an Array
, containing the key and value of the inner Hash
, the second is the value from the combined ctr
Array
. By using mass assignment I can give the key and value their own variable name.
Since the logic is a bit to much for a one liner I moved it to a lambda, but this could also be a function (when passing h2 as param).