I've been using a library called Djangular recently, which integrates Django and Angular.

After a while I thought 'Wouldn't it be fun to programmatically generate portmanteau?'

Here are some examples (e.g. testcases):

Django + Angular -> Djangular
Camel + Leopard -> Cameleopard
Spoon + Fork -> Spoork
Ham + Hammer -> Hammer

The general rule is to find the first common letter working backwards on first word, and working forwards on the last word. Not all portmanteau can be found this way though...

I've deliberately picked 'simple' ones to generate (for instance we're ignoring things like Zebra + Donkey = Zedonk, which is too arbitrary to generate programmatically - the correct behaviour).

Ties can be resolved eitherway - suppose you have cart + ante - both carte, (since t is the last common letter in the first word) and cante, (since a is the first common letter of the last word) are value. Unfun loopholes are not allowed (including pulling 'solved' examples off wikipedia)

This is code golf, so shortest answer wins. I'll pick the winner a month from posting. Please post the output for all your code.

  • \$\begingroup\$ Could you elaborate on the rules for double letters? It seems that they are omitted if they appear in the first word but are kept if they appear in the second? Or is it that double vowels are omitted but double consonants are kept? \$\endgroup\$ Commented Aug 7, 2015 at 7:20
  • 2
    \$\begingroup\$ Read literally, "latest in first word, earliest in last word" would mean Spoon + Fork -> Spoork if we take the second o to be the latest in the first word. Similarly, the common t would mean Croissant + Donut -> Croissant. Another example, with earliest in last word this time, would be mock + cocktail = mococktail. I'm not sure if I'm just missing something major or... \$\endgroup\$
    – Sp3000
    Commented Aug 7, 2015 at 12:29
  • 2
    \$\begingroup\$ This question is still very confusing. The title asks for the longest overlap, and I'm not sure what the body of the question is asking for, but whatever it is seems to favour shorter overlaps. \$\endgroup\$ Commented Aug 7, 2015 at 17:28
  • 1
    \$\begingroup\$ Perhaps we could use a system like this for choosing which overlap is best: the final 'score' of each option is calculated with (final word length + letters overlapped) * (1 - abs(amount of first word used - amount of second word used)). For example, django + angular = djangular uses 5/6 of django and all of angular, a difference of 1/6. Subtract that from (the final length of 9 + the overlap of 3), and you get a total score of 10, by far the highest possible. I'd be glad to clarify any questions about this system! \$\endgroup\$ Commented Aug 7, 2015 at 18:02
  • 1
    \$\begingroup\$ I still don't understand this part: The general rule is to find the first common letter working backwards on first word, and working forwards on the last word. Does one of them take priority? \$\endgroup\$
    – xnor
    Commented Aug 7, 2015 at 22:04

8 Answers 8


Python 3, 128 79 bytes

Let me know if I got anything wrong and critiques are always welcome.

def f(a,b):
    if a[-1]==b[0]:return a[:-1]+b
    if a[c]==a[c-1]:c-=1
    return a[:c]+b[1:]

Called with f(word 1,word 2) and returns the portmanteau. Will throw an error if the inputs aren't compatible. Works for all test cases.


If I've read the edits to this question correctly, this updated code should work

f=lambda a,b:a[:-1]+b if a[-1]==b[0] else a[:len(a)-1-a[::-1].find(b[1])]+b[1:]

CoffeeScript, 111 bytes/characters


l="toLowerCase";f=(a,b)->return a.substr(0,i)+b.substr(j)[l]()for c,j in b when(i=a[l]().lastIndexOf(c[l]()))>0

Try it online!

To use:


I hoped it will be shorter, but.... ok, still better than PHP :D

The examples result in (using the alert statement above):

Djangular Cameleopard Spoork Hammer

Explanation (Might not be valid coffescript):

l="toLowerCase"; # Keeps code shorter (and DRY :D )
  return a.substr(0,i)+b.substr(j)[l]() # Return the result
     for c,j in b # Postfix for loop
         # Run the return statement only when the 
         # character `c` is found (also stores its last position in `i`)

Haskell, 61 bytes

Doesn't handle case, but that doesn't seem to be a big deal? Tell me if it is.

a!b=maybe(init a!b)((a++).(`drop`b))$last a`lookup`zip b[1..]

Then "Django"!"Angular" is "Djangular".

  • \$\begingroup\$ Oh my, I am horrible at this. Agree that case makes this challenge "ugly", so 61 vs my 99... I learn to use lookup now, and infixes. And I just confused myself cause infix operator notation in Haskell ^= code in markdown. \$\endgroup\$ Commented Aug 20, 2015 at 18:19

rs, 24 bytes

(.*)(.).* .*?(\2.*)/\1\3

To copy the initial post from @refi64 here's the live demo and all test cases. The only improvement is that you can spare the \2 back reference by including it in the third group.

I didn't want to pollute with an answer that's actually a comment, but some person on Stack Exchange decided that you can't comment when you're a newcomer and that edits should be refused, because that should obviously be a comment. Thus, sorry for that misplaced content.

  • 2
    \$\begingroup\$ Hello, and welcome to PPCG! I'll comment for you if you want, and when you get 50 rep, you can comment too. However, Stack Exchange has a Be Nice policy, and unfortunately calling someone retarded is against the policy. \$\endgroup\$ Commented Jul 28, 2017 at 18:01
  • \$\begingroup\$ P.S. You can comment on your own posts. \$\endgroup\$ Commented Jul 29, 2017 at 6:39
  • \$\begingroup\$ xD They thought it all through! Thanks for the comment :D \$\endgroup\$ Commented Jul 29, 2017 at 8:35

PHP (270 bytes)

function portmanteau($s1, $s2){ $r = ''; $y = false; foreach(str_split($s1) as $c1){ $r .= $c1; foreach(str_split($s2) as $c2){ if($y){ $r.= $c2; } if(strtolower($c1)==strtolower($c2)){ $y = true; continue; } } if($y){ break; } } if(!$y) $r .= $s2; return ucfirst($r); }


function portmanteau($str1, $str2){
    $resultingString = '';

    $letterMatched = false;
    foreach(str_split($str1) as $char){
        $resultingString .= $char;

        foreach(str_split($str2) as $char2){
                $resultingString.= $char2;

                $letterMatched = true;
    if(!$letterMatched) $resultingString .= $str2;

    return ucfirst($resultingString);


echo portmanteau('Croissant', 'Donut');

Tested and works with the examples in the question.

  • 1
    \$\begingroup\$ What will it output ? Coissonut ? \$\endgroup\$ Commented Aug 7, 2015 at 11:20
  • \$\begingroup\$ No, "Cronut". It iterates/loops the characters in the first word first, then the second word. \$\endgroup\$
    – Dzhuneyt
    Commented Aug 8, 2015 at 18:36

Haskell, 148 bytes

C/case isn't restored though. Without case this was 99...

import Data.Char
o a b=|a==""=b|0<1=a
p a""=""
p a b|last a==head b=a++tail b|0<1=o(p(init a)b)(p a(tail b))
z a b=p(t a)(t b)
t=map toLower
  • \$\begingroup\$ You can make o, p, and z infix operators (try !, #, %) to save some bytes everywhere (as a!b=... is shorter than o a b=...). \$\endgroup\$
    – lynn
    Commented Aug 20, 2015 at 16:39

Perl, 138

I'm sure someone can optimize this further
($a,$b,$l,$r)=(@ARGV,0);for$A(split//,$a){$r.=$A;for$B(split//,$b){$r.=$B if$l;if($A=~/$B/i){$l=1;next;}}last if$l;}$r.=$b if!$l;

138 w/ optimizations and output
($a,$b,$l,$r)=(@ARGV,0);for$A($a=~/./g){$r.=$A;for$B($b=~/./g){$r.=$B if$l;if($A=~/$B/i){$l=1;next;}}last if$l;}$r.=$b if!$l;print"$r\n";


rs, 26 bytes

(.*)(.).* .*?\2(.*)/\1\2\3

Live demo and all test cases.

So far this is the shortest answer!

The underlying logic is pretty simple. It just matches greedily on the first word (to get the last common character) and non-greedily on the second (to get the first common character). It then replaces the string with everything but the text in between the two characters.

  • \$\begingroup\$ @CyrilLemaire posted this: (.*)(.).* .*?(\2.*)/\1\3 (but does not have enough rep to comment) \$\endgroup\$ Commented Jul 28, 2017 at 18:02

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.