I need to do many methods on lnk.href to get the f_name.
I want to write the code that way, but it gave me
undefined method `gsub!' for nil:NilClass (NoMethodError)
If I don't want to write these in one line (as it's hard to read), what's a better way in Ruby?
f_name = lnk.href.split('/').last
.gsub!(/[(]+/, "_")
.gsub!(/[)]+/, "_")
String#gsub! returns nil if there's no match:
'1'.gsub!(/2/, '_')
# => nil
'1'.gsub!(/2/, '_').gsub!(/1/, '_')
# NoMethodError: undefined method `gsub!' for nil:NilClass
# from (irb):6
# from C:/Ruby200-x64/bin/irb:12:in `<main>'
Replace gsub! with gsub will probably solve your problem:
'1'.gsub(/2/, '_')
# => "1"
'1'.gsub(/2/, '_').gsub(/1/, '_')
# => "_"
This is not due to the syntax but only because some methods return nil in your case.
So, if I understand correctly, you are trying to write some of the methods you want to chain on a new line to make it more readable? You can't break a method call down to a new line all by itself, as you've discovered, since now Ruby thinks you are trying to call a method on nothing.
I'm operating on the assumption that what you are trying to accomplish is something like this:
lnk = Some Link
f_name = lnk.href.split('/').last.gsub!(/[(]+/, "_").gsub!(/[)]+/, "_")
So, you're trying to find and break up links in HTML by splitting them at the /, then pulling out and manipulating the last part of the URL.
There are a couple things you can do to make this more readable and logical. One is to fix your regex. Because you're using .gsub!, you are already searching for all matching occurrences, so the + is unnecessary. You can also combine your two calls to .gsub! into one like this: .gsub!(/[()]/, "_")
That will match and substitute all occurrences of either the open or close paren, making your chain one method shorter.
For the rest, I would suggest breaking this into two steps at the logical place: between creating a data structure, and manipulating the data within it. First, create the array of substrings:
f_name = lnk.href.split('/')
Then manipulate the data from that array:
manipulated_substring = f_name.last.gsub!(/[()]/, "_")
That will make your code more readable, and keep your data intact!
Related
I have a variable that represents a path:
path = "/foo/bar"
I want to remove the last part of the path. I tried it with gsub! like this:
path.gsub!("/bar","")
but I also want to throw an error if "/bar" isn't at the end of the string. I also tried path.split("/"), but this seems not very memory efficient. The method is called a lot, so an in-place approach would be perfect. Another variation would be to only remove every string until "/" is hit, without throwing the error.
What would be a fast and memory efficient method to do this?
You could use a Regexp to match only at the end of the string:
'/foo/bar'.gsub!(/\/bar\z/, '')
#=> '/foo'
Since gsub! returns nil if there wasn't a match, just combine it with raising an error:
'/foo/blub'.gsub!(/\/bar\z/, '') || raise(StandardError)
#=> StandardError: StandardError
To get what you want, you can do:
File.dirname("/foo/bar")
# => "/foo"
To raise an error is a different thing:
raise unless "/foo/bar".end_with?("/bar")
You could use String#rindex to find the last occurrence of / and use that value to get the preceding sub-string:
path[0, path.rindex("/")]
I need to puts a string like
puts "#{movie.title}, #{movie.imdb_rating.round(3)}, #{movie.imdb_ratings_count}"
So the output will be Gone Girl, 8.23, 302532
However, these are quite tedious since I always need to add #{} and "",
What is a good way to quick puts, without always adding #{} and ""?
I suggest constructing an array and using the join method.
def printStrings do *strings
strings.join(', ')
end
printStrings 'apple', 'banana', 'orange' #'apple, banana, orange'
Alternatively, you can utilize a special variable, called the output field separator, which you can access as $,. (Its default value is nil)
$, = ', '
print 'apple', 'banana', 'orange' #'apple, banana, orange'
$, = nil
This method is outlined in the docs for print
It would make sense to add a method to the Movie class:
class Movie
def to_desc
"##title, #{#imdb_rating.round(3)}, ##imdb_ratings_count"
end
end
LukeP's answer is good if you want the same punctuation between each value but if you want something with a varying format, eg Gone Girl - 8.23, 302532, you can use ruby's % operator for string formating:
"%s - %s, %s" % [movie.title, movie.imdb_rating.round(3), movie.imdb_ratings_count]
Ruby's printing is solid but not amazing. Have you looked at the Awesome Print gem?
https://github.com/michaeldv/awesome_print
It's what I use to do elegant printing. It's a little unclear exactly how you're asking to format it, but this gems seems like it'll give you enough options to solve your problem.
Let's say I have a literal Fixnum 1420028751000 and I want to convert using this:
Time.at(1420028751000 / 1000) # => 2014-12-31 20:25:51 +0800
I can put spaces also, let's say I am beginner and my coding is bad:
Time.at(1420028751000 / 1000)
Time.at(1420028751000 /1000)
All these work fine and give me correct result.
However, once I introduced variable:
a = 1420028751000
Time.at(a/1000) # => Works!
Time.at(a / 1000) # => Works!
Time.at(a /1000) # => Strange thing happen
My question is, what is so unique about the /1000 that make it not working when introducing an variable in Ruby?
Ruby's parser is interpreting /1000 as the beginning of a literal regexp, since a is a token which may be a method. That is, imagine that a is a method:
def a(arg); end
Time.at(a /1000)
Ruby will interpret this as "a invoked with an incomplete regexp as the argument". To "complete" this call, Ruby is expecting you might want to do something like:
Time.at( a(/1000/) )
I'm looking at ruby's replace: http://www.ruby-doc.org/core/classes/String.html#M001144
It doesn't seem to make sense to me, you call replace and it replaces the entire string.
I was expecting:
replace(old_value, new_value)
Is what I am looking for gsub then?
replace seems to be different than in most other languages.
I agree that replace is generally used as some sort of pattern replace in other languages, but Ruby is different :)
Yes, you are thinking of gsub:
ruby-1.9.2-p136 :001 > "Hello World!".gsub("World", "Earth")
=> "Hello Earth!"
One thing to note is that String#replace may seem pointeless, however it does remove 'taintediness". You can read more up on tained objects here.
I suppose the reason you feel that replace does not make sense is because there is assigment operator = (not much relevant to gsub).
The important point is that String instances are mutable objects. By using replace, you can change the content of the string while retaining its identity as an object. Compare:
a = 'Hello' # => 'Hello'
a.object_id # => 84793190
a.replace('World') # => 'World'
a.object_id # => 84793190
a = 'World' # => 'World'
a.object_id # => 84768100
See that replace has not changed the string object's id, whereas simple assignment did change it. This difference has some consequences. For example, suppose you assigned some instance variables to the string instance. By replace, that information will be retained, but if you assign the same variable simply to a different string, all that information is gone.
Yes, it is gsub and it is taken from awk syntax. I guess replace stands for the internal representation of the string, since, according to documentation, tainted-ness is removed too.
In other languages, in RegExp you can use /.../g for a global match.
However, in Ruby:
"hello hello".match /(hello)/
Only captures one hello.
How do I capture all hellos?
You can use the scan method. The scan method will either give you an array of all the matches or, if you pass it a block, pass each match to the block.
"hello1 hello2".scan(/(hello\d+)/) # => [["hello1"], ["hello2"]]
"hello1 hello2".scan(/(hello\d+)/).each do|m|
puts m
end
I've written about this method, you can read about it here near the end of the article.
Here's a tip for anyone looking for a way to replace all regex matches with something else.
Rather than the //g flag and one substitution method like many other languages, Ruby uses two different methods instead.
# .sub — Replace the first
"ABABA".sub(/B/, '') # AABA
# .gsub — Replace all
"ABABA".gsub(/B/, '') # AAA
use String#scan. It will return an array of each match, or you can pass a block and it will be called with each match.
All the details at http://ruby-doc.org/core/classes/String.html#M000812