-4

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'

Artjom B.
  • 61,146
  • 24
  • 125
  • 222
s c
  • 71
  • 1
  • 11
  • 1
    What is your question? – sawa Apr 11 '17 at 11:59
  • @sawa - if the value in array `ctr` is 'cobol' then for the corresponding element's value (index wise) in hash `h1` needs to be converted. for example index of cobol in array ctr is [0,5] 0 and 5 elements in hash `h1` are 'ABC' and 'OPP', the corresponding values in the hash should be updated as per the following logic: if the last character of the values in hash `h1` exists in the hash h2, then replace it with corresponding key's value[last char] from hash `h2` for example: 10000100126N should be changed to -100001001265 I need to achieve the above. – s c Apr 11 '17 at 12:50

2 Answers2

2

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).

3limin4t0r
  • 19,353
  • 2
  • 31
  • 52
1

You're trying to match an array and a hash, which is just going to cause you problems. It would be easier if you changed ctr to a hash as well:

ctr = {"ABC" => "cobol", "CDE" => nil, "TPP" => nil, "APD" => "test", "PRODUCTID" => nil, "OPP" => "cobol", "CTC" => nil}

Then at least you can match by keys.

Even better would be to start creating an object instead of using your hash. Once you start nesting collection objects this deep, it's time to create some kind of object to make your life easier.

David Stanley
  • 333
  • 3
  • 12