RJones
RJones

Reputation: 41

How can I extract the domain from an email without using Regex, in Ruby?

Given an email address in the form, '[email protected]' or '[email protected]', how can one extract only the domain bar (not bar.com), without resorting to regex or a specialist library?

This post: 'How to get domain from email' almost answers my question, but I am unsure how to split domain 'bar.com', or whether another approach might exist.

Upvotes: 1

Views: 1519

Answers (4)

Todd A. Jacobs
Todd A. Jacobs

Reputation: 84363

Partition an Email Address Using Fixed-String Methods

There are a number of string-based approaches, but one of the easiest (at least in my opinion) is to use String#rpartition to grab what you need after the terminal @ symbol in the address. For example, given an address stored in an email variable:

# Get domain-parts.
email.rpartition('@').last

# Get domain parts without the TLD.
email.rpartition('@').last.rpartition('.').first

This is simple, reliable, and (most importantly) relatively easy to read.

The Rightmost-Partition Method in Action

To see String#rpartition in action, and to see how flexible it is even with subdomains or atypical local-parts, consider the results of the following:

emails = %w[
  [email protected]
  [email protected]
  [email protected]
  [email protected]
]

emails.map { |email| email.rpartition(?@).last.rpartition(?.).first }
#=> ["bar", "bar", "bar.baz", "subdomain.example"]

Use Partitioning for Destructured Assignments

One benefit of using String#partition or String#rpartition over String#split is that the two methods provide a natural fit for destructuring assignments. For example:

email = "[email protected]"

local_part, _, domain_part = email.rpartition ?@
#=> ["foo+extension.address", "@", "subdomain.example.com"]

hostname, _, top_level_domain = domain_part.rpartition ?.
#=> ["subdomain.example", ".", "com"]

Because destructuring gives you access to each part in a separate variable, this approach could be useful in creating a variety of alternative representations for your email addresses, such as a hash of domains and the local-parts within each domain. As a trivial example, consider:

address_list = Hash.new { |k,v| k[v] = [] }

emails = %w[[email protected] [email protected] [email protected]]

emails.each do |email|
  local_part, _, domain_part = email.rpartition ?@
  address_list[domain_part] << local_part
end

address_list
#=> {"example.com"=>["foo", "bar"], "other.example.com"=>["baz"]}

Obviously, you could make the structure as simple or as complex as you want. However, complicated structures like {"com"=>{"example"=>["foo"], "other.example"=>["bar"]}} are unwieldy, and there's probably an X/Y problem with a simpler solution available. Nevertheless, it's useful to know you can do these sorts of things with de- and restructuring.

Upvotes: 1

pdoherty926
pdoherty926

Reputation: 10359

You can achieve this using String#split.

'[email protected]'.split('@')[1].split('.')[0] # => bar

... or if you need to support subdomains:

'[email protected]'. split('@')[1]. reverse. split('.'). drop(1). map(&:reverse). reverse. join('.') # => qux.bar

Upvotes: 0

Cary Swoveland
Cary Swoveland

Reputation: 110685

Here are four valid email email addresses:

valid_email_addresses = ["[email protected]", "[email protected]", "\"[email protected]\"@c.com", "a@b"]

See the Wiki for "Email address".

We can extract the desired strings with the following method.

def domain_preface(str)
  # something here like: return nil unless email_address_valid?(str)
  s = str[str.rindex('@')+1..-1]
  i = s.rindex('.')
  return "" unless i
  s[0..s.rindex('.')-1]
end

valid_email_addresses.each { |s| puts "%s: |%s|" % [s, domain_preface(s)] }
[email protected]: |b|
[email protected]: |b.c|
"[email protected]"@c.com: |c|
a@b: ||

Upvotes: 0

Draugr
Draugr

Reputation: 436

Use the split function:

mystring = "bar.com"
mystring.split('.') --> ["bar", "com"]

This even works if the string has subdomains:

mystring = "night.bar.com"
mystring.split('.') --> ["night", "bar", "com"]

edit: Oops, two minutes too late :)

Upvotes: 0

Related Questions