Goal: Create a combination of emails based from inputted first name, last name, middle name, and a domain. Add in common separators. Then I'll check which one is correct with the rapportive API. This is the first part of the bigger script.

If you are given the string variables


How would you create the following?


at the moment I am solving it by creating an array for each permutation

fi_perms = [fi].product ['_' + li,                                                                                                       
                           '_' + ln,                                                                                                       
                           '-' +li,                                                                                                        
                           '-' ln,                                                                                                         
                           '.' + li,                                                                                                       
                           '.' + ln,                                                                                                       
                           mi '_' ln,                                                                                                      
                           mi '-' ln,                                                                                                      
                           mi + '.' + ln,                                                                                                  
                           mi + ln]                                                                                                        
  fn_perms = [fn].product ['_' + li,                                                                                                       
                           '_' + ln,                                                                                                       
                           '_' + mi '_' + ln,                                                                                              
                           '_' + mn '_' + ln,                                                                                              
                           '-' + li,                                                                                                       
                           '-' + ln,                                                                                                       
                           '-' + mi + '-' + ln,                                                                                            
                           '-' + mn + '-' ln,                                                                                              
                           '.' + li,                                                                                                       
                           '.' + ln,                                                                                                       
                           '.' + mi '.' +ln,                                                                                               
                           '.' + mn '.' ln,                                                                                                
                           mi + ln,  
                           mi + ln,                                                                                                        
                           mn + ln]                                                                                                        
  li_perms = [li].product ['_' + fi,                                                                                                       
                           '_' + fn,                                                                                                       
                           '-' + fi,                                                                                                       
                           '-' + fn,                                                                                                       
                           '.' + fi,                                                                                                       
                           '.' + fn,                                                                                                       
  ln_perms = [ln].product [ln,                                                                                                             
                           '_' + fi,                                                                                                       
                           '_' + fn,                                                                                                       
                           '-' + fi,                                                                                                       
                           '-' + fn,                                                                                                       
                           '.' + fi,                                                                                                       
                           '.' + fn,                                                                                                       

because I will be using it later by adding it to another array like so

perms = li_perms + ln_perms + fi_perms + fn_perms
permutations = []

perms.count.times do |i|
    perms.each do |perm|
        permutations[i] = perm.join

permutations[0] = ['fn.mn.ln']

Is there a better of doing this?

  • 1
    \$\begingroup\$ Is there any pattern in the expected permutations? \$\endgroup\$
    – tokland
    Mar 4, 2014 at 9:12
  • \$\begingroup\$ shouldn't the second {fn}{mi}{ln} be {fn}.{mi}{ln}? and is the order required in that way exactly? \$\endgroup\$
    – Vogel612
    Mar 4, 2014 at 10:32
  • \$\begingroup\$ @Vogel612 it does not have to be exact. \$\endgroup\$
    – Patrick
    Mar 4, 2014 at 17:38
  • 4
    \$\begingroup\$ Wild guess here — first name, last name, first initial, last initial, middle initial, middle name? What are you really trying to accomplish, and why don't you ask that instead? \$\endgroup\$ Mar 4, 2014 at 17:59
  • 1
    \$\begingroup\$ @200_success Create a combination of emails based from inputted first name, last name, middle name, and a domain. Add in common separators. Then I'll check which one is correct with the rapportive API. This is the first part of the bigger script. \$\endgroup\$
    – Patrick
    Mar 4, 2014 at 18:23

2 Answers 2


Since your list is not "all permutation", but is painstakingly built by hand, I would not suggest using array's permutation API or something like that, but keep the curated mode you are using.

I would suggest building it in a more readable way. In your way of [fi].product[...] it is very hard to follow which permutation exists, and which doesn't. The first list you show is more readable, and if you name your atoms correctly (first_name instead of fn), it makes it trivial to understand what you are trying to do. I would suggest building your permutation table as a string like this:

name_permutations = <<PERMS

And then use substitutions to get all permutations:

name_permutations.gsub('{first_name}', first_name)
                 .gsub('{last_name}', last_name)
                 .gsub('{middle_name}', middle_name)
                 .gsub('{first_initial}', first_initial)
                 .gsub('{middle_initial}', middle_initial)
                 .gsub('{last_initial}', last_initial)
  • \$\begingroup\$ I like this approach for the question posed, although I think the all possible permutations of names, initials, and separators is a more interesting problem. \$\endgroup\$
    – Jonah
    Mar 8, 2014 at 6:55
  • 1
    \$\begingroup\$ @Jonah - For that you could (1..9).map{ |i| [fi,fn,mi,mn,li,ln,'.','-','_'].permutations(i).map(&:join) }.flatten (ruby-doc.org/core-2.1.1/Array.html#method-i-permutation) \$\endgroup\$
    – Uri Agassi
    Mar 8, 2014 at 7:29
  • \$\begingroup\$ Good point that's clever \$\endgroup\$
    – Jonah
    Mar 8, 2014 at 14:34
  • \$\begingroup\$ @UriAgassi just a minor error that permutations should be singular permutation \$\endgroup\$
    – Patrick
    Mar 10, 2014 at 3:35

Edit: I've made some changes to try to improve the readability of my answer.

You could just create a few simple helper methods. Here's an example, based on the assumption that ordering is not important. Rather than the usual approach of presenting the code and then showing how it is used, I have reversed those steps, as the code is so simple that most readers will be able to glean it merely from its application.

There are two helper methods, gen and middle_with_seps, The variables fn, mn and ln refer to "first name", "middle name" and "last name". The first, middle and last initials are: fi = fn[0], mi = mn[0] and li = ln[0]. The constants should be self-explanatory. (The application of the code is best appreciated when one of these is playing in the background.)


fn, mn, ln = 'Wild', 'Bill', 'Hickok'

gen([fn, fi], DASH_USCORE                                    , [ln, li] ) +
gen([ln, li], DASH_USCORE                                    , [fn, fi] ) +
gen(li      , DOT                                            , fn       ) +
gen(li      , NOSPACE_DOT                                    , fi       ) +
gen(fn      , middle_with_seps(mi, DASH_USCORE + NOSPACE_DOT), ln       ) +
gen(fn      , middle_with_seps(mn, DASH_USCORE + NOSPACE_DOT), ln       ) +
gen(fi      , middle_with_seps(mi, DASH_USCORE + NOSPACE)    , ln       ) +
gen(fi      , middle_with_seps(mi+'.', NOSPACE)              , ln       )

#=> ["Wild_Hickok", "Wild_H", "Wild-Hickok", "Wild-H",
#      "W_Hickok", "W_H", "W-Hickok", "W-H",
#    "Hickok_Wild", "Hickok_W", "Hickok-Wild", "Hickok-W",
#      "H_Wild", "H_W", "H-Wild", "H-W",
#    "H.Wild", "HW", "H.W",
#    "Wild_B_Hickok", "Wild-B-Hickok", "WildBHickok", "Wild.B.Hickok",
#    "Wild_Bill_Hickok", "Wild-Bill-Hickok","WildBillHickok","Wild.Bill.Hickok",
#    "W_B_Hickok", "W-B-Hickok", "WBHickok",
#    "WB.Hickok"] 


NOSPACE     = ['']
DOT         = ['.']
DASH_USCORE = ['-', '_']

fi, mi, li = fn[0], mn[0], ln[0]

def combine_strings(s1, s2, s3) "#{s1}#{s2}#{s3}" end

def gen(left_strings, seps, right_strings)
  left_strings  = [left_strings].flatten
  seps          = [seps].flatten
  right_strings = [right_strings].flatten
  left_strings.each_with_object([]) { |l,a| seps.each { |sep|
    right_strings.each { |r| a << combine_strings(l, sep, r) } } }

def middle_with_seps(m, seps)
  seps.each_with_object([]) { |s,a| a << combine_strings(s, m, s) }  


gen()'s arguments are as follows:

  • left_strings : an array of strings for the left end of the string
  • seps : an array of separators (e.g., '', '.', '-', etc.)
  • right_strings: an array of strings for the right end of the string

If left_strings, seps or right_strings is entered as a string, rather than an array of strings, it is converted to an array containing itself. gen constructs an array of strings, one for each combination of strings taken from left_strings, seps and right_strings.

middle_with_seps's arguments are as follows:

  • m is the middle name or initial
  • seps is the same as for the method gen

middle_with_seps creates an array of separators that is passed as the argument seps in gen() when the middle name or initial is to be included. Each element of that array is the (string) value of m bracketed by a separator (except in one case where the initial 'B' is converted to 'B.' ('WB.Hickok'). For example,

middle_with_seps(mi, DASH_USCORE + NOSPACE) #=> ["_B_", "-B-", "B"]
  • \$\begingroup\$ Why are you giving such unreadable names to your variables and functions? Your code is very hard to read, and you need to tell us the la is an array for the left end of the string, instead of calling it left_end_strings or something more meaningful - this is code-review, after all... \$\endgroup\$
    – Uri Agassi
    Mar 16, 2014 at 7:08
  • \$\begingroup\$ @Uri, there was a method to my madness, even if I have miscalculated, as I appear to have done (for the author of code is usually not the best one to judge its readability). My intent was for the lines beginning gen([fn, fi], ['_', '-']... to tell the reader at a glance what the two helpers did. That wouldn't have worked if I had generate_combinations([first_name, last_name], ['_', '-']... as lines would have wrapped or required horizontal scrolling, defeating the purpose. I've made some changes to my answer. I welcome suggestions for further improvement. Thanks. \$\endgroup\$ Mar 16, 2014 at 18:54

Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.