This is a job for expl3.
\documentclass{article}
\usepackage{amsmath,xparse}
\ExplSyntaxOn
\NewDocumentCommand{\diagonal}{O{b}m}
{% #1 = fences, #2 = entries, comma separated
\egreg_diagonal:nn { #1 } { #2 }
}
\seq_new:N \l__egreg_diagonal_entries_seq
\seq_new:N \l__egreg_diagonal_row_seq
\cs_new_protected:Nn \egreg_diagonal:nn
{
\seq_set_from_clist:Nn \l__egreg_diagonal_entries_seq { #2 }
\begin{#1matrix}
\int_step_function:nN { \seq_count:N \l__egreg_diagonal_entries_seq } \__egreg_diagonal:n
\end{#1matrix}
}
\cs_new_protected:Nn \__egreg_diagonal:n
{% #1 = row number
\seq_clear:N \l__egreg_diagonal_row_seq
\int_step_inline:nn { \seq_count:N \l__egreg_diagonal_entries_seq }
{
\int_compare:nTF { #1 == ##1 }
{
\seq_put_right:Nx \l__egreg_diagonal_row_seq
{
\seq_item:Nn \l__egreg_diagonal_entries_seq { #1 }
}
}
{
\seq_put_right:Nn \l__egreg_diagonal_row_seq { 0 }
}
}
\seq_use:Nn \l__egreg_diagonal_row_seq { & } \\
}
\ExplSyntaxOff
\begin{document}
\[
\diagonal{1} \qquad \diagonal[p]{1,2} \qquad
\diagonal[B]{1,2,3} \qquad \diagonal[v]{1,2,3,4}
\]
\[
\diagonal{\lambda_1,\lambda_2,\lambda_3,\lambda_4,\lambda_5,
\lambda_6,\lambda_7,\lambda_8,\lambda_9}
\]
\end{document}
Here I exploit the fact that \int_step_function:nN delivers its result “all at once”, so we can build the whole matrix starting in its first cell.
The optional argument states the shape of the fences, in the same style as amsmath: b (default) for brackets, p for parentheses, B for braces, v for vertical lines and V for double vertical lines.
The mandatory argument is a comma separated list of the diagonal entries.
The outer loop builds a row at each step. It calls an inner loop that adds entries to a sequence: 0 if we're not at the diagonal, the right entry otherwise. Next the sequence is delivered with & between each item and \\ for ending the row.

Warning: the empty diagonal matrix cannot be produced. A test whether the list of entries is empty can be added: change the main function to
\cs_new_protected:Nn \egreg_diagonal:nn
{
\seq_set_from_clist:Nn \l__egreg_diagonal_entries_seq { #2 }
\begin{#1matrix}
\int_compare:nTF { \seq_count:N \l__egreg_diagonal_entries_seq == 0 }
{
{\:}
}
{
\int_step_function:nN { \seq_count:N \l__egreg_diagonal_entries_seq } \__egreg_diagonal:n
}
\end{#1matrix}
}
and the call \diagonal{} would produce

A different implementation where one can choose whether to input explicit diagonal elements or just follow a pattern.
\documentclass{article}
\usepackage{amsmath,xparse}
\ExplSyntaxOn
\NewDocumentCommand{\diagonal}{m}
{
\group_begin:
\keys_set:nn { egreg/diagonal } { #1 }
\egreg_diagonal:
\group_end:
}
\keys_define:nn { egreg/diagonal }
{
type .tl_set:N = \l__egreg_diagonal_type_tl,
type .initial:n = b,
entries .clist_set:N = \l_egreg_diagonal_entries_clist,
pattern .code:n = \cs_set_protected:Nn \__egreg_diagonal_pattern:n { #1 },
items .int_set:N = \l__egreg_diagonal_items_int,
}
\seq_new:N \l__egreg_diagonal_entries_seq
\seq_new:N \l__egreg_diagonal_row_seq
\cs_generate_variant:Nn \seq_set_from_clist:Nn { NV }
\cs_new_protected:Nn \egreg_diagonal:
{
\cs_if_exist:NTF \__egreg_diagonal_pattern:n
{
\int_step_inline:nn { \l__egreg_diagonal_items_int }
{
\seq_put_right:Nn \l__egreg_diagonal_entries_seq { \__egreg_diagonal_pattern:n { ##1 } }
}
}
{
\seq_set_from_clist:NV \l__egreg_diagonal_entries_seq \l_egreg_diagonal_entries_clist
}
\begin{\l__egreg_diagonal_type_tl matrix}
\int_compare:nTF { \seq_count:N \l__egreg_diagonal_entries_seq == 0 }
{
{\:}
}
{
\int_step_function:nN { \seq_count:N \l__egreg_diagonal_entries_seq } \__egreg_diagonal:n
}
\end{\l__egreg_diagonal_type_tl matrix}
}
\cs_new_protected:Nn \__egreg_diagonal:n
{% #1 = row number
\seq_clear:N \l__egreg_diagonal_row_seq
\int_step_inline:nn { \seq_count:N \l__egreg_diagonal_entries_seq }
{
\int_compare:nTF { #1 == ##1 }
{
\seq_put_right:Nx \l__egreg_diagonal_row_seq { \seq_item:Nn \l__egreg_diagonal_entries_seq { #1 } }
}
{
\seq_put_right:Nn \l__egreg_diagonal_row_seq { 0 }
}
}
\seq_use:Nn \l__egreg_diagonal_row_seq { & } \\
}
\ExplSyntaxOff
\begin{document}
\[
\diagonal{entries=} \qquad
\diagonal{entries=1} \qquad \diagonal{type=p,entries={1,2}} \qquad
\diagonal{type=B,entries={1,2,3}} \qquad \diagonal{type=v,entries={1,2,3,4}}
\]
\[
\diagonal{pattern=\lambda_{#1},items=9}
\]
\end{document}
The output is the same as before.