Columnar Transposition Cipher
Python 3, 188 bytes.
There's gotta be a saner way to do this.
from itertools import*
def f(a,b):a,c,s=a.upper(),''.join,sorted;return c(map(c,zip_longest(*[v for k,v in s([(v,b[i::len({*a})])for i,v in enumerate(s({*a},key=a.find))])],fillvalue='')))
Test cases:
assert f('goat', 'Hello World!') == 'lHelWo odrl!'
assert f('GOAT', 'Hello World!') == 'lHelWo odrl!'
assert f('GOAATAT', 'Hello World!') == 'lHelWo odrl!'
assert f('CBABD', 'Duplicates are ignored.') == 'puDlacit sea eriongr.de'
Well that was a fun journey back to square one.
MATL, 12 bytes
kune2M&SY)1e
Try it online!
Explanation
The code works using rows instead of columns. This is totally equivalent and fits better with MATL's column-major indexing.
k % Take first input (key) implicitly. Convert to lowercase
u % Keep unique chars, stably
n % Number of unique chars
e % Take second input (message) implicitly. Reshape into char matrix
% with the above number of rows, padding with zeros if needed
2M % Push string with unique chars of key again
&S % Sort and push the indices of the sorting
Y) % Use as row indices into char matrix
1e % Reshape into a row. Zeros are displayed as spaces