Although expl3's mapping-functions seem not to be designed for doing 'in place'-manipulation on lists of tokens, in the following I assume that the LaTeX-distribution in use is recent enough for \clist_map_function:NN to be available. (interface3.pdf of my TeX-distribution says about \clist_map_function:NN: Updated: 2012-06-29. This is about 11 years ago, so assuming availability of \clist_map_function:NN might be reasonable.)
The gist of all following code is applying \clist_map_function:NN for mapping comma-list-items to a function/macro which uses an argument-delimiter for placing items nested in braces behind that delimiter. Before the delimiter you find the comma-list item in braces and the tokens forming the next iteration of the mapping-function.
So after each mapping-iteration you have s. th. like
\function{⟨item in this mapping-iteration⟩}⟨stuff for subsequent mapping-iterations⟩\DELIMITER{⟨item from last mapping-iteration⟩}{⟨item from last but one mapping-iteration⟩}..{⟨item from first mapping-iteration⟩}.
And \function uses arguments #1 and #2\DELIMITER and does:
⟨stuff for subsequent mapping-iterations⟩\DELIMITER{⟨item in this mapping-iteration⟩}{⟨item from last mapping-iteration⟩}{⟨item from last but one mapping-iteration⟩}..{⟨item from first mapping-iteration⟩}.
So by and by from items,of,list,a and ITEMS,OF,LIST,B, s.th. like
\DELIMITER{⟨result gathered so far, initially empty⟩}{B}{LIST}{OF}{ITEMS}{},{a}{list}{of}{items}{},
is formed.
This in turn can be merged by using a loop where \DELIMITER is defined with an argument-pattern #1#2#3,#4#5, →
\DELIMITER{{#2}{#4}#1}#3,#5, and which terminates when {#3#5} is an empty argument so that {#2}{#4}#1 forms the result of merging.
Caveat 1:
⟨stuff for subsequent mapping-iterations⟩ must not contain \DELIMITER unless nested in curly braces.
I didn't bother checking the implementation of \clist_map_function:NN for finding out if the case of ⟨stuff for subsequent mapping-iterations⟩ containing \DELIMITER outside a pair of curly braces is possible.
Caveat 2:
Applying the function which is mapped to the single comma-list-items leads to accumulating things in the input stack. Therefore with (not that) large comma-lists you might get error-messages like
! TeX capacity exceeded, sorry [input stack size=...].
In the code below \mymodule_mergecommalists:nnwnw is used in place of \DELIMITER and \mymodule_moveargbehindmergecommalistsbraced:nw is used in place of \function/is mapped to the comma-list's items.
If you wish the content of \l__mymodule_keys_clist and \l__mymodule_acts_clist to be hardcoded into the definition of \TestFunction so that the behavior of \TestFunction is not affected by subsequent changes to \l__mymodule_keys_clist and/or \l__mymodule_acts_clist, you can probably do s. th. like this:
\documentclass{article}
\ExplSyntaxOn
% \DefineTestFunction{<control word token denoting test function to define>}%
% {<additional xparse-argument-specifiers, denoting #2, #3 etc>
% % #1 is available in <action if #1 is not a key of the keys comma list>
% % in any case and denotes the argument to test
% }%
% {<keys comma list>}%
% {<actions comma list>}%
% {<action if #1 is not a key of the keys comma list>}%
\cs_new:Npn \mymodule_DefineTestFunction:NnNNn #1#2#3#4#5 {
\exp_args:Nno \use:n
{ \NewDocumentCommand { #1 } { m#2 } } {
\exp:w \exp_args:Nno \use:n {\exp_end: \str_case:nnF {##1} }
{%
\exp:w
\mymodule_moveargbehindmergecommalistsunbraced:nw{{},}
\clist_map_function:NN #4 \mymodule_moveargbehindmergecommalistsbraced:nw
\mymodule_moveargbehindmergecommalistsunbraced:nw{{},}
\clist_map_function:NN #3 \mymodule_moveargbehindmergecommalistsbraced:nw
\mymodule_moveargbehindmergecommalistsbraced:nw{}
\mymodule_mergecommalists:nnwnw
}{#5}
}
}
\cs_new:Npn \mymodule_moveargbehindmergecommalistsunbraced:nw #1#2\mymodule_mergecommalists:nnwnw
{#2 \mymodule_mergecommalists:nnwnw #1}
\cs_new:Npn \mymodule_moveargbehindmergecommalistsbraced:nw #1#2\mymodule_mergecommalists:nnwnw
{#2 \mymodule_mergecommalists:nnwnw {#1}}
\cs_new:Npn \mymodule_mergecommalists:nnwnw #1#2#3,#4#5,
{\tl_if_empty:nTF{#3#5}{\exp_end:{#2}{#4}#1}{\mymodule_mergecommalists:nnwnw {{#2}{#4}#1}#3,#5,}}
%-----------------------------------------------------------------------------------
\clist_set:Nn \l__mymodule_keys_clist { key1, key2, key3 }
\clist_set:Nn \l__mymodule_acts_clist { act1, act2, act3 }
\mymodule_DefineTestFunction:NnNNn{\TestFunction}{}
{\l__mymodule_keys_clist}
{\l__mymodule_acts_clist}
{The~key~#1~is~not~in~the~list,~thus~default~action!}
\mymodule_DefineTestFunction:NnNNn{\OtherTestFunction}{m}
{\l__mymodule_keys_clist}
{\l__mymodule_acts_clist}
{The~key~#1~is~not~in~the~list,~thus:~#2}
\cs_show:c{TestFunction~code}
\cs_show:c{OtherTestFunction~code}
\ExplSyntaxOff
\begin{document}
\TestFunction{key1}
\TestFunction{key2}
\TestFunction{key3}
\TestFunction{key4}
\OtherTestFunction{key1}{This time's default}
\OtherTestFunction{key2}{This time's default}
\OtherTestFunction{key3}{This time's default}
\OtherTestFunction{key4}{This time's default}
\end{document}

If you wish the lists \l__mymodule_keys_clist and \l__mymodule_acts_clist to be evaluated at the time when \TestFunction is carried out, you can probably do s. th. like this:
\documentclass{article}
\ExplSyntaxOn
% \DefineTestFunction{<control word token denoting test function to define>}%
% {<additional xparse-argument-specifiers, denoting #2, #3 etc>
% % #1 is available in <action if #1 is not a key of the keys comma list>
% % in any case and denotes the argument to test
% }%
% {<keys comma list>}%
% {<actions comma list>}%
% {<action if #1 is not a key of the keys comma list>}%
\cs_new:Npn \mymodule_DefineTestFunction:NnNNn #1#2#3#4#5 {
\NewDocumentCommand { #1 } { m#2 } {
\exp_args:Nno \str_case:nnF {##1}
{%
\exp:w
\mymodule_moveargbehindmergecommalistsunbraced:nw{{},}
\clist_map_function:NN #4 \mymodule_moveargbehindmergecommalistsbraced:nw
\mymodule_moveargbehindmergecommalistsunbraced:nw{{},}
\clist_map_function:NN #3 \mymodule_moveargbehindmergecommalistsbraced:nw
\mymodule_moveargbehindmergecommalistsbraced:nw{}
\mymodule_mergecommalists:nnwnw
}{#5}
}
}
\cs_new:Npn \mymodule_moveargbehindmergecommalistsunbraced:nw #1#2\mymodule_mergecommalists:nnwnw
{#2 \mymodule_mergecommalists:nnwnw #1}
\cs_new:Npn \mymodule_moveargbehindmergecommalistsbraced:nw #1#2\mymodule_mergecommalists:nnwnw
{#2 \mymodule_mergecommalists:nnwnw {#1}}
\cs_new:Npn \mymodule_mergecommalists:nnwnw #1#2#3,#4#5,
{\tl_if_empty:nTF{#3#5}{\exp_end:{#2}{#4}#1}{\mymodule_mergecommalists:nnwnw {{#2}{#4}#1}#3,#5,}}
%-----------------------------------------------------------------------------------
\mymodule_DefineTestFunction:NnNNn{\TestFunction}{}
{\l__mymodule_keys_clist}
{\l__mymodule_acts_clist}
{The~key~#1~is~not~in~the~list,~thus~default~action!}
\mymodule_DefineTestFunction:NnNNn{\OtherTestFunction}{m}
{\l__mymodule_keys_clist}
{\l__mymodule_acts_clist}
{The~key~#1~is~not~in~the~list,~thus:~#2}
\clist_set:Nn \l__mymodule_keys_clist { key1, key2, key3 }
\clist_set:Nn \l__mymodule_acts_clist { act1, act2, act3 }
\cs_show:c{TestFunction~code}
\cs_show:c{OtherTestFunction~code}
\ExplSyntaxOff
\begin{document}
\TestFunction{key1}
\TestFunction{key2}
\TestFunction{key3}
\TestFunction{key4}
\OtherTestFunction{key1}{This time's default}
\OtherTestFunction{key2}{This time's default}
\OtherTestFunction{key3}{This time's default}
\OtherTestFunction{key4}{This time's default}
\end{document}

If you wish to specify at the time when \TestFunction is carried out, which comma-list-variables to evaluate, you can probably do s. th. like this:
\documentclass{article}
\ExplSyntaxOn
% \DefineTestFunction{<control word token denoting test function to define>}%
% {<additional xparse-argument-specifiers, denoting #4, #5 etc>
% % #1 is available in <action if #1 is not a key of the keys comma list>
% % in any case and denotes the argument to test.
% % #2 is available in <action if #1 is not a key of the keys comma list>
% % in any case and denotes the <keys comma list>.
% % #3 is available in <action if #1 is not a key of the keys comma list>
% % in any case and denotes the <actions comma list>.
% }%
% {<action if #1 is not a key of the keys comma list>}%
\cs_new:Npn \mymodule_DefineTestFunction:NnNNn #1#2#3 {
\NewDocumentCommand { #1 } { mmm#2 } {
\exp_args:Nno \str_case:nnF {##1}
{%
\exp:w
\mymodule_moveargbehindmergecommalistsunbraced:nw{{},}
\clist_map_function:NN ##3 \mymodule_moveargbehindmergecommalistsbraced:nw
\mymodule_moveargbehindmergecommalistsunbraced:nw{{},}
\clist_map_function:NN ##2 \mymodule_moveargbehindmergecommalistsbraced:nw
\mymodule_moveargbehindmergecommalistsbraced:nw{}
\mymodule_mergecommalists:nnwnw
}{#3}
}
}
\cs_new:Npn \mymodule_moveargbehindmergecommalistsunbraced:nw #1#2\mymodule_mergecommalists:nnwnw
{#2 \mymodule_mergecommalists:nnwnw #1}
\cs_new:Npn \mymodule_moveargbehindmergecommalistsbraced:nw #1#2\mymodule_mergecommalists:nnwnw
{#2 \mymodule_mergecommalists:nnwnw {#1}}
\cs_new:Npn \mymodule_mergecommalists:nnwnw #1#2#3,#4#5,
{\tl_if_empty:nTF{#3#5}{\exp_end:{#2}{#4}#1}{\mymodule_mergecommalists:nnwnw {{#2}{#4}#1}#3,#5,}}
%-----------------------------------------------------------------------------------
\mymodule_DefineTestFunction:NnNNn{\TestFunction}{}
{The~key~#1~is~not~in~the~list,~thus~default~action!}
\mymodule_DefineTestFunction:NnNNn{\OtherTestFunction}{m}
{The~key~#1~is~not~in~the~list,~thus:~#4}
\clist_set:Nn \l__mymodule_keys_clist { key1, key2, key3 }
\clist_set:Nn \l__mymodule_acts_clist { act1, act2, act3 }
\cs_show:c{TestFunction~code}
\cs_show:c{OtherTestFunction~code}
\ExplSyntaxOff
\begin{document}
\ExplSyntaxOn
\TestFunction{key1}{\l__mymodule_keys_clist}{\l__mymodule_acts_clist}\par
\TestFunction{key2}{\l__mymodule_keys_clist}{\l__mymodule_acts_clist}\par
\TestFunction{key3}{\l__mymodule_keys_clist}{\l__mymodule_acts_clist}\par
\TestFunction{key4}{\l__mymodule_keys_clist}{\l__mymodule_acts_clist}\par
\OtherTestFunction{key1}{\l__mymodule_keys_clist}{\l__mymodule_acts_clist}{This~time's~default}\par
\OtherTestFunction{key2}{\l__mymodule_keys_clist}{\l__mymodule_acts_clist}{This~time's~default}\par
\OtherTestFunction{key3}{\l__mymodule_keys_clist}{\l__mymodule_acts_clist}{This~time's~default}\par
\OtherTestFunction{key4}{\l__mymodule_keys_clist}{\l__mymodule_acts_clist}{This~time's~default}
\end{document}

\TestFunction{key2}and\TestFunction{key4}should be. – Mico Jul 20 '23 at 04:53