I have this array of hashes:
array = [{:ID=>"AAA", :Quantity=>1}, {:ID=>"BBB@ST050", :Quantity=>2}]
If the :ID
key doesn't have the "@" character then I add the new key/value pair :Stage => "Finished
, otherwise the stage is the number after the characters "@ST", :Stage => "050
. This is what I would like to obtain.
# => [{:ID=>"AAA", :Stage=>"Finished", :Quantity=>1}, {:ID=>"BBB", :Stage=>"050", :Quantity=>2}]
What can I do?
This is my foolish (but still working) code that relies only on Array
operations but the initial array is different. I'm searching for a smart solution starting from the array of hashes.
array=[["AAA", "1"], ["BBB@ST050", "2"]]
for n in 0...array.length
#######################################################################################
if array[n][0].include?("@") == true
array[n].insert(1, array[n][0].byteslice((array[n][0].length-3)..array[n][0].length))
array[n][0]=array[n][0].byteslice(0...(array[n][0].length-6))
else
array[n].insert(1, "Finished")
end
#######################################################################################
for i in 0...array[n].length
# create couple of array
array[n][i]=array[n][i].split
end
array[n][2][0]=array[n][2][0].to_i
# insert the key
array[n][0].insert(0, :ID)
array[n][1].insert(0, :Stage)
array[n][2].insert(0, :Quantity)
# transform inside array into hash
array[n]=array[n].to_h
end
# => [{:ID=>"AAA", :Stage=>"Finished", :Quantity=>1}, {:ID=>"BBB", :Stage=>"050", :Quantity=>2}]
CodePudding user response:
You can just iterate over the array of hashes with #map
, modifying the hash if needed to add the expected key/value pair, or using a regular expression I can extract the needed info and set up the value for the :Stage
key appropriately. Using #map!
instead modifies the existing array.
array = [{:ID=>"AAA", :Quantity=>1}, {:ID=>"BBB@ST050", :Quantity=>2}]
array.map! do |h|
if h[:ID] !~ /@/
h[:Stage] = "Finished"
else
h[:Stage] = h[:ID][/(?<=@ST)\d{3}/]
end
h
end
This is one place you might want to use the ternary operator.
array.map! { |h| h[:Stage] = h[:ID] !~ /@/ ? "Finished" : h[:ID][/(?<=@ST)\d{3}/]; h }
As noted in comments, since we're modifying the hashes, but not replacing them, #each
will also work.
array.each { |h| h[:Stage] = h[:ID] !~ /@/ ? "Finished" : h[:ID][/(?<=@ST)\d{3}/] }
As requested, an explanation of h[:ID][/(?<=@ST)\d{3}/]
:
h[:ID]
is accessing the value in the hash associated with the key :ID
. /(?<=@ST)\d{3}/
is a regular expession. It's looking for any three digits (\d{3}
) that follow @ST
.
By feeding this regular expression to the #[]
method on the string we get from h[:ID]
we get the matching text. In the example, this is "050"
.
CodePudding user response:
You can iterate the array through an each method, I use scan method and two joins, so you can extract all the numbers after the @ in your :Id value. Here is my code.
array.each do |hash|
if !hash[:ID].match?(/@/)
hash[:Stage] = "Finished"
else
hash[:Stage] = hash[:ID].scan(/@\w /).join('').scan(/\d/).join('')
end
end
I hope it can be helpful to you.