g | x | w | all
Bytes Lang Time Link
01205AB1E241004T140058ZKevin Cr
093jq240605T154539ZGammaFun
204Dart240604T001116ZRedz
069R240522T114857Zpajonk
168F#240603T221441ZCiaran_M
012Japt240529T210509ZShaggy
015Brachylog240523T085337ZFatalize
055Arturo240522T110357Zchunes
058Ruby240523T053852ZG B
032Charcoal240521T152042ZNeil
077JavaScript Node.js240521T142650Zl4m2
084Perl 5 a240521T230536ZXcali
054Haskell240521T170848Zmatteo_c
059Python 3240521T151209ZMukundan
011Jelly240521T162529ZUnrelate

05AB1E, 12 bytes

ãʒÙ`¨s¦‚åJT-

Try it online or verify all test cases.

Explanation:

ã            # Cartesian product to create all possible pairs of the (implicit) input-list
 ʒ           # Filter it by:
             #  For valid pairs:
  Ù          #   Uniquify, which is a no-op for valid pairs
   `         #   Pop and push both words separately to the stack
    ¨        #   Remove the last letter from the top word
     s       #   Swap so the first word is at the top
      ¦      #   Remove the first letter of this word
       ‚     #   Pair the two strings together
        å    #   Check for both strings whether they're in the (implicit) input-list
         J   #   Join this pair of checks together
          T- #   Subtract 10
             #   (only 1 is truthy in 05AB1E)
             #  For pairs using the same word twice:
  Ù          #   Uniquify, so a singleton remains
   `         #   Pop and push this word to the stack
    ¨        #   Remove the last letter from this word
     s       #   Swap so the (implicit) input-list is at the top of the stack
      ¦      #   Remove the first string from the list
       ‚     #   Pair the list together with the word
        å    #   Check for each inner string whether they're in the (implicit) input-list
         J   #   Join the inner list of checks together
          T- #   Subtract 10 from values in the pair
             #   (a pair will always be falsey, since only 1 is truthy in 05AB1E)
             # (after which the list of valid pairs is output implicitly)

jq, 93 bytes

. as $L|def m(f):map(select(f|IN($L[])));[m(.[1:]),m(.[:-1])]|combinations|select(.[0]!=.[1])

Attempt This Online!

. as $L |                     # Bind the full list to $L
def m(f):                     # Function m selects all elements which are in
  map(select(f | IN($L[])));  # the list after applying the given filter

[m(.[1:]), m(.[:-1])]         # Generate lists of possible first and second words
| combinations                # Take the Cartesian product of the two lists
| select(.[0] != .[1])        # Filter out any ["foo","foo"] cases

Dart, 206 204 bytes

List<dynamic>f(var l){var x=[];for(var i=0;i<l.length;i++)for(var j=0;j<l.length;j++)if(l[i]!=l[j]&&l.contains(l[i].substring(1))&&l.contains(l[j].substring(0,l[j].length-1)))x.add([l[i],l[j]]);return x;}

Goes through all pairs of words, checks whether they're a valid pair, returns the answer as a list of pairs.

Previous (thanks Kevin for -2 bytes):

List<dynamic>f(var l){var x=[];for(var i=0;i<l.length;i++)for(var j=0;j<l.length;j++)if(l[i]!=l[j])if(l.contains(l[i].substring(1))&&l.contains(l[j].substring(0,l[j].length-1)))x.add([l[i],l[j]]);return x;}

R, 76 69 bytes

Edit: -1 byte thanks to @int 21h -- Glory to Ukraine --.

\(W,`?`=\(i)W[sub(i,"",W)%in%W],x=merge(?"^.",?".$"))x[x[,1]!=x[,2],]

Attempt This Online!

The final filtering condition looks ugly to me, but I can't think of anything shorter.

F#, 168 bytes

let p(d:string seq)=Seq.allPairs(d|>Seq.where(fun w->Seq.contains(w.Substring 1)d))(d|>Seq.where(fun w->Seq.contains(w.Remove(w.Length-1))d))|>Seq.where(fun(f,l)->f<>l)

Gets the two sets of words, pairs them with Seq.allPairs, and finally removes the duplicates. Had to use some strong typing, otherwise there would be compiler errors.

Try it online!

Japt, 14 12 bytes

I'm stupidly out of practice; I'm sure there's a better way.

ï
f@m¸øX¸s1J

Try it

Brachylog, 15 bytes

{⊇Ċp.bʰkᵗ∈ᵛ?∧}ᶠ

Try it online!

Explanation

{              }ᶠ      Find all:
 ⊇Ċ                     Take a couple of 2 words [a, b] from the input
   Ċp.                   Output is either [a, b] or [b, a]
     .bʰkᵗ               Remove the first letter of a and the last letter of b
                         (or vice-versa if the output is [b, a])
          ∈ᵛ?∧          Verify that these 2 new words are in the original input

Arturo, 55 bytes

$->_[permutate.by:2_|select'p->_⊃@[drop p\0chop p\1]]

Requires modern Arturo, hence the online playground won't run this yet. Here's a screenshot of running this locally:

enter image description here

Explanation

$->_[...]        ; a function taking an arg named _
permutate.by:2_  ; get permutations of length two
|                ; then...
select'p->       ; select pairs p where...
_⊃               ; the input is a superset of...
@[...]           ; a list with...
drop p\0         ; first word in pair w/o first letter
chop p\1         ; second word in pair w/o last letter

Ruby, 58 bytes

->w{w.permutation(2).select{|a,b|[a[1..-1],b.chop]-w==[]}}

Try it online!

Charcoal, 34 32 bytes

WS⊞υιFυEΦυ¬∨⁼ικ⁻⟦Φιν✂κ⁰±¹⟧υ⁺⁺ι κ

Try it online! Link is to verbose version of code. Explanation:

WS⊞υι

Input the words.

Fυ

Loop over all of the words.

EΦυ¬∨⁼ικ⁻⟦Φιν✂κ⁰±¹⟧υ⁺⁺ι κ

Loop over all of the words except where both words are the same or either the suffix of the first word or the prefix of the second word are not present in the list.

JavaScript (Node.js), 77 bytes

f=(x,a)=>x.flatMap(b=>a!=b&x.includes(b.slice(!a,a&&-1))?a?[[a,b]]:f(x,b):[])

Try it online!

-3B from Arnauld

Perl 5 -a, 84 bytes

$"=',';map{/(.(.*)),((.*).)/;say if$1ne$3&&(grep$2eq$_,@F)*grep$4eq$_,@F}<{@F},{@F}>

Try it online!

Haskell, 54 bytes

f w=[[x,y]|x<-w,y<-w,x/=y,elem(tail x)w,elem(init y)w]

Attempt This Online!

Python 3, 59 bytes

lambda d:[[i,j]for i in d for j in d-{i}if{i[1:],j[:-1]}<d]

Try it online!

Jelly, 11 bytes

œ!2ḊṖƭ€fƑ¥Ƈ

Try it online!

Don't love resorting to ƭ for this.

œ!2            Permutations without replacement of 2 elements.
          Ƈ    Filter the pairs to those which
       fƑ      are unchanged by filtering to the wordlist
         ¥     after
   Ḋ ƭ€        removing the first character of the first word
    Ṗ          and the last character of the second.