Ruby Gsub Problem When Using Backreference and Hashes

Ruby gsub problem when using backreference and hashes

String#gsub! has two forms, one in which you pass in a string as the second argument, in which variable references like $1 and $2 are replaced by the corresponding subexpression match, and one in which you pass in a block, which is called with arguments which have the subexpression matches passed in. You are using the block form when calling gsub!, but the string in your hash is attempting to use the form in which a string is passed in.

Furthermore, the variable interpolation in your string is occurring before the match; variable interpolation happens as soon as the string is evaluated, which is at the time your hash is being constructed, while for this to work you would need variable interpolation to happen after the subexpression replacement happens (which is never the case; variable interpolation will happen first, and the resulting string would be passed in to gsub! for gsub! to substitute the subexpression match for $1, but $1 would have already been evaluated and no longer in the string, as the interpolation has already occurred).

Now, how to fix this? Well, you probably want to store your blocks directly in the hash (so that the strings won't be interpreted while constructing the hash, but instead when gsub! invokes the block), with an argument corresponding to the match, and $1, $2, etc. bound to the appropriate subexpression matches. In order to turn a block into a value that can be stored and later retrieved, you need to add lambda to it; then you can pass it in as a block again by prefixing it with &:

hash = {
/(\d+) years/ => lambda { "#{$1.to_f*2} days" },
/cannot/ => lambda { 'of course we CAN' }
}

hash.each {|k,v|
a.gsub!(k, &v)
}

How to access Hash Map inside string using backreference

You can use a block, then the global variables $1 .. $9 hold matches. This achieves what you want:

map = { 'stack' => 'overflow' }
p "stackoverflow".gsub(/(stack)(overflow)/) { map[$1] + $2 }
# => overflowoverflow

Alternatively, you can just pass a variable to the block:

map = { 'stack' => 'overflow' }
p "stackoverflow".gsub(/stack/) { |match| map[match] }
# => overflowoverflow

See String#gsub for reference.

Using Ruby's gsub with a string containing \0

Escape it with another backslash so that gsub will know you want "\\0".

"replace me".gsub(/replace me/, "this \\\\0 is a test")

(Edit) if by "\0" you meant the byte 0x00, do this:

"replace me".gsub(/replace me/, "this \0 is a test")

Ruby 1.9 regex as a hash key

It will not work without some extra code, as it is you are comparing a Regexp object with either an Integer or a String object. They won't be value equal, nor identity equal. They would match but that requires changes to the Hash class code.

irb(main):001:0> /(\d+)/.class
=> Regexp
irb(main):002:0> 2222.class
=> Fixnum
irb(main):003:0> '2222'.class
=> String
irb(main):004:0> /(\d+)/==2222
=> false
irb(main):007:0> /(\d+)/=='2222'
=> false
irb(main):009:0> /(\d+)/.equal?'2222'
=> false
irb(main):010:0> /(\d+)/.equal?2222
=> false

you would have to iterate the hash and use =~ in something like:

 hash.each do |k,v|    
unless (k=~whatever.to_s).nil?
puts v
end
end

or change the Hash class to try =~ in addition to the normal matching conditions. (I think that last option would be difficult, in mri the Hash class seems to have a lot of C code)

Regex can this be achieved

You will need to use some sort of logic.

str += ',location_manager' unless str.gsub!(/location_manager,/,'')

I'm assuming that if it's not present you append it to the end of the string

How do I write a regular expression that will match characters in any order?

Here is your solution

^(?:([act])(?!.*\1)){3}$

See it here on Regexr

^                  # matches the start of the string
(?: # open a non capturing group
([act]) # The characters that are allowed and a capturing group
(?!.*\1) # That character is matched only if it does not occur once more, Lookahead assertion
){3} # Defines the amount of characters
$

The only special think is the lookahead assertion, to ensure the character is not repeated.

^ and $ are anchors to match the start and the end of the string.

How to elegantly symbolize_keys for a 'nested' hash

There are a few ways to do this

  1. There's a deep_symbolize_keys method in Rails

    hash.deep_symbolize_keys!

  2. As mentioned by @chrisgeeq, there is a deep_transform_keys method that's available from Rails 4.

    hash.deep_transform_keys(&:to_sym)

    There is also a bang ! version to replace the existing object.

  3. There is another method called with_indifferent_access. This allows you to access a hash with either a string or a symbol like how params are in the controller. This method doesn't have a bang counterpart.

    hash = hash.with_indifferent_access

  4. The last one is using JSON.parse. I personally don't like this because you're doing 2 transformations - hash to json then json to hash.

    JSON.parse(JSON[h], symbolize_names: true)

UPDATE:

16/01/19 - add more options and note deprecation of deep_symbolize_keys

19/04/12 - remove deprecated note. only the implementation used in the method is deprecated, not the method itself.

Why do dict and lru_cache treat keys that all hash to 0 differently?

Dictionaries don't only use the hash to determine if elements are distinct, they are also checked for equality.

Duplicate elements are elements which are equal, as determined by __eq__. The behavior you see can be explained by the fact that 0 == "" returns False.

>>> 0 == 0.0
True
>>> 0 == 0j
True
>>> 0 == False
True
>>> 0 == ""
False


Related Topics



Leave a reply



Submit