Home > database >  How to do operations on strings in array of hashes in ruby?
How to do operations on strings in array of hashes in ruby?

Time:04-26

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.

  • Related