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