8
\$\begingroup\$

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

{fn}
{ln}
{fi}
{li}
{mi}
{mn}

How would you create the following?

{fn}
{ln}
{fn}{ln}
{fn}.{ln}
{fi}{ln}
{fi}.{ln}
{fn}{li}
{fn}.{li}
{fi}{li}
{fi}.{li}
{ln}{fn}
{ln}.{fn}
{ln}{fi}
{ln}.{fi}
{li}{fn}
{li}.{fn}
{li}{fi}
{li}.{fi}
{fi}{mi}{ln}
{fi}{mi}.{ln}
{fn}{mi}{ln}
{fn}.{mi}.{ln}
{fn}{mn}{ln}
{fn}.{mn}.{ln}
{fn}-{ln}
{fi}-{ln}
{fn}-{li}
{fi}-{li}
{ln}-{fn}
{ln}-{fi}
{li}-{fn}
{li}-{fi}
{fi}{mi}-{ln}
{fn}-{mi}-{ln}
{fn}-{mn}-{ln}
{fn}_{ln}
{fi}_{ln}
{fn}_{li}
{fi}_{li}
{ln}_{fn}
{ln}_{fi}
{li}_{fn}
{li}_{fi}
{fi}{mi}_{ln}
{fn}_{mi}_{ln}
{fn}_{mn}_{ln}

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

fi_perms = [fi].product ['_' + li, 
 '_' + ln, 
 '-' +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, 
 li, 
 ln, 
 mi + ln, 
 ln, 
 mi + ln, 
 mn + ln] 
 li_perms = [li].product ['_' + fi, 
 '_' + fn, 
 '-' + fi, 
 '-' + fn, 
 '.' + fi, 
 '.' + fn, 
 fi, 
 fn] 
 ln_perms = [ln].product [ln, 
 '_' + fi, 
 '_' + fn, 
 '-' + 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
 end
end
permutations[0] = ['fn.mn.ln']

Is there a better of doing this?

asked Mar 4, 2014 at 5:54
\$\endgroup\$
6
  • 1
    \$\begingroup\$ Is there any pattern in the expected permutations? \$\endgroup\$ Commented 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\$ Commented Mar 4, 2014 at 10:32
  • \$\begingroup\$ @Vogel612 it does not have to be exact. \$\endgroup\$ Commented 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\$ Commented 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\$ Commented Mar 4, 2014 at 18:23

2 Answers 2

3
\$\begingroup\$

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
{last_initial}{first_name}
{last_initial}.{first_name}
{last_initial}{first_initial}
{last_initial}.{first_initial}
{first_initial}{middle_initial}{last_name}
{first_initial}{middle_initial}.{last_name}
{first_name}{middle_initial}{last_name}
{first_name}.{middle_initial}.{last_name}
{first_name}{middle_name}{last_name}
{first_name}.{middle_name}.{last_name}
{first_name}-{last_name}
{first_initial}-{last_name}
{first_name}-{last_initial}
{first_initial}-{last_initial}
{last_name}-{first_name}
{last_name}-{first_initial}
{last_initial}-{first_name}
{last_initial}-{first_initial}
...
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)
 .split($/)
answered Mar 7, 2014 at 21:21
\$\endgroup\$
4
  • \$\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\$ Commented 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\$ Commented Mar 8, 2014 at 7:29
  • \$\begingroup\$ Good point that's clever \$\endgroup\$ Commented Mar 8, 2014 at 14:34
  • \$\begingroup\$ @UriAgassi just a minor error that permutations should be singular permutation \$\endgroup\$ Commented Mar 10, 2014 at 3:35
1
\$\begingroup\$

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.)

Application

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"] 

Code

NOSPACE = ['']
DOT = ['.']
NOSPACE_DOT = 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) } } }
end
def middle_with_seps(m, seps)
 seps.each_with_object([]) { |s,a| a << combine_strings(s, m, s) } 
end

Explanation

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"]
answered Mar 16, 2014 at 0:32
\$\endgroup\$
2
  • \$\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\$ Commented 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\$ Commented Mar 16, 2014 at 18:54

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.