4
\$\begingroup\$

I have an input for users to enter a Twitter account in any different way and I want to extract the user account.

For example:

twitters = [
 "www.twitter.com/twitteruser1",
 "@twitteruser2",
 "twitteruser3",
 "https://twitter.com/twitteruser4",
 "https://www.twitter.com/twitteruser5",
 "www.twitter.com/twitteruser6",
 "http://www.twitter.com/twitteruser7",
 "http://www.twitter.com/twitteruser8",
 "twitter.com/twitteruser9"
]

The script that I've written to extract the data is the following:

twitters.each do |twitter|
 # for the url
 twitter_user = twitter.match(/twitter.com\/([^\/.]*)$/)
 if twitter_user != nil
 puts twitter_user[1]
 next
 end
 # for @ beginning
 twitter_user = twitter.match(/^@([^\/.]*)$/)
 if twitter_user != nil
 puts twitter_user[1]
 next
 end
 # if we arrive, we haven't found any coincidence
 puts twitter
end

It actually works, outputting the following:

twitteruser1
twitteruser2
twitteruser3
twitteruser4
twitteruser5
twitteruser6
twitteruser7
twitteruser8
twitteruser9

But as I'm really newbie in Ruby I wanted to check for possible improvements.

Jamal
35.2k13 gold badges134 silver badges238 bronze badges
asked Dec 15, 2016 at 16:41
\$\endgroup\$

3 Answers 3

2
\$\begingroup\$

I would put the regular expressions into a list:

TWITTER_PATTERNS = [
 /twitter.com\/([^\/.]*)$/, # "www.twitter.com/twitteruser1"
 /^@([^\/.]*)$/, # "@twitteruser2"
]

This function can iterate over that list:

def twitter_user(twitter)
 TWITTER_PATTERNS.each do |pattern|
 return 1ドル if twitter =~ pattern
 end
 twitter
end

This clearly separates the "policy" (what patterns are used to extract twitter users) from the "mechanism" (the loop we use to apply the policy). A change to the function's implementation won't require a change to the patterns, and vice versa.

answered Dec 17, 2016 at 22:41
\$\endgroup\$
2
\$\begingroup\$

You can use a ruby case statement to check for different regex matches. If a match is found, it is by definition not nil, so it removes the need for a nil check as well.

twitters.each do |twitter|
 case twitter
 # for the url
 when /twitter.com\/([^\/.]*)$/
 puts 1ドル
 # for @ beginning
 when /^@([^\/.]*)$/
 puts 1ドル
 # if we arrive, we haven't found any coincidence
 else
 puts twitter
 end
end

When ruby does a regex match, any capture groups are assigned to the global variables 1ドル, 2ドル, 3ドル, and so on. See this question on StackOverflow for more details about the mechanic.

Note on your regex: If you want the capture groups to actually contain anything, you should change the asterisk to a plus: ([^\/.]+). Without the plus, "www.twitter.com/" will be captured by one of the regex. With the plus, that string will fall into the default case.

answered Dec 15, 2016 at 19:38
\$\endgroup\$
0
\$\begingroup\$
twitters.each do |twitter|
 puts twitter.match(/twitter.com\/([^\/.]*)$/) ||
 twitter.match(/^@([^\/.]*)$/) ||
 twitter
end

It's shorter, and I'd say at least equally readable. Chain of || operators will return first truthy value.

answered Dec 15, 2016 at 17:18
\$\endgroup\$
2
  • \$\begingroup\$ Hi @Borsunho, thanks for the quick response. The problem is that in the two first cases, I need the value of the twitter.match(...)[1], and if I access to [1] without checking if twitter.match(...) is nil, for the accounts that haven't matched the first case, the script fails with a: undefined method [] for nil:NilClass \$\endgroup\$ Commented Dec 15, 2016 at 17:26
  • \$\begingroup\$ I suspect there is a bug here. If I'm right, it would be fixed by appending &.()[1] to each of the two match expressions, but the fix makes the code pretty ugly. There's probably a better fix. \$\endgroup\$ Commented Dec 19, 2016 at 12:44

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.