Home > Mobile >  Switching words in string with keywords from hash problem
Switching words in string with keywords from hash problem

Time:09-16

I am trying to create method to switch words in string with keywords from hash. For example, there is the string:

my_string = "France france USA usa ENGLAND england ENGland"

Here is my hash:

my_hash = {"england" => "https://google.com"}

And there is the loop:

occurrences = {}
my_string.gsub!(/\w /) do |match|
  key = my_hash[match.downcase]
  count = occurrences.store(key, occurrences.fetch(key, 0).next)

  count > 2 ? match : "<a href = #{key}>#{match}</a>"
end

The output of this loop is:

 <a href = >France</a> <a href = >france</a> USA usa <a href = https://google.com>ENGLAND</a> <a href = https://google.com>england</a> ENGland

Expected output:

France france USA usa <a href = https://google.com>ENGLAND</a> <a href = https://google.com>england</a> ENGland

The problem you see here is that my loop always took over an <a href> tag the first two words from string, no matter if they are in the hash or not (as you can see in 'France' example) and it should work as in 'England' example (the first two 'Englands' became a hyperlinks but not the third, as it should work).

P.S - additional question: is there any way to avoid already existing hyperlinks in string and not to touch them? For example - if there already would be an 'England' hyperlink in string but with another href.

CodePudding user response:

my_string = "France france USA usa ENGLAND england ENGland"
my_hash = {"england"=>"https://google.com"}
my_string.split
         .chunk(&:downcase)
         .flat_map do |country,a|
            a.flat_map.with_index do |s,i|
              if i < 2 && my_hash.key?(country)    
                "<a href = #{my_hash[country]}>#{s}</a>"
              else
                s    
              end
            end
          end.join(' ')
  #=> "France france USA usa <a href = https://google.com>ENGLAND</a> <a href = https://google.com>england</a> ENGland"

See Enumerable#chunk and Enumerable#flat_map.

Note that

enum0 = my_string.split.chunk(&:downcase)
  #=> #<Enumerator: #<Enumerator::Generator:0x00007ff90c13bc28>:each>

The values generated by this enumerator can be seen by converting it to an array.

enum0.to_a
  #=> [["france", ["France", "france"]], ["usa", ["USA", "usa"]],
  #    ["england", ["ENGLAND", "england", "ENGland"]]]

Then

enum1 = enum0.flat_map
  #=> #<Enumerator: #<Enumerator: #<Enumerator::Generator:0x00007ff90c113e58>:each>:flat_map>

The initial value generated by enum1 and assigned to the two block variables is as follows.

country, a = enum1.next
  #=> ["france", ["France", "france"]] 
country
  #=> "france"
a #=> ["France", "france"]

CodePudding user response:

It isn't 100% clear to me from your question what the desired output is, but if you want to only replace words that match a key in your hash, simply add an if (or a next) after your hash lookup. Also, the variable key was used to store this looked up value, so I renamed it and incremented the key not the value in the occurrences hash. This seemed to be more in-line with what you want.

occurrences = {}
my_string.gsub!(/\w /) do |match|
  key = match.downcase
  value = my_hash[key]
  next unless value

  count = occurrences.store(key, occurrences.fetch(key, 0).next)

  count > 2 ? match : "<a href = #{value}>#{match}</a>"
end
  • Related