Supongamos que tengo una lista definida mediante \def\zList{0,0,1,1,1,2,2,10}
.
¿Cómo puedo obtener una lista (separada por comas) 0,1,2,10
que contenga los valores únicos/distintos en 0,0,1,1,1,2,2,10
?
¿Quizás haya alguna manera de hacerlo pgfmath
?(También necesito todo en contextos más complejos [pgfplots/-table], así que pgfmath
no sería tan malo...)
\documentclass[a4paper]{article}
\usepackage{tikz}
\begin{document}
\def\zList{0,0,1,1,1,2,2,10}
\let\List=\empty% create List
\foreach \n in \zList {%
\pgfmathparse{\n}% <--- A clever method needed here
\ifx\empty\List{} \xdef\List{\pgfmathresult}%
\else \xdef\List{\List,\pgfmathresult}%
\fi}
Show Zero List: \zList
Show List-Actual: \List
Show List-Target: 0,1,2,10
\end{document}
Respuesta1
Podrías eliminar duplicados usandoexpl3
\ExplSyntaxOn
\cs_new_eq:NN \removeclistdupes \clist_remove_duplicates:N
\ExplSyntaxOff
\Removeclistdupes\List
Un ejemplo completo
\documentclass[a4paper]{article}
\usepackage{expl3}
\ExplSyntaxOn
\cs_new_eq:NN \removeclistdupes \clist_remove_duplicates:N
\ExplSyntaxOff
\usepackage{tikz}
\begin{document}
\def\zList{0,0,1,1,1,2,2,10}
\let\List=\empty% create List
zList
\foreach \n in \zList {%
\pgfmathparse{\n}% <--- A clever method needed here
\ifx\empty\List{} \xdef\List{\pgfmathresult}%
\else \xdef\List{\List,\pgfmathresult}%
\fi}
\removeclistdupes\List
Show Zero List: \zList
Show List-Actual: \List
Show List-Target: 0,1,2,10
\end{document}
Respuesta2
\documentclass{article}
\usepackage{listofitems,pgffor}
\newcounter{zcount}
\newtoks\mytoks
\mytoks{}
\expandafter\def\csname zmatch0\endcsname{-9999999}% NUMBER NOT IN LIST
\begin{document}
\def\zList{0,0,1,1,1,2,2,10}
The original list is \zList
\readlist\zdata{\zList}
\foreachitem\z\in\zdata[]{%
\gdef\ztest{F}%
\foreach\zcnt in {0,...,\thezcount}{%
\ifnum\z=\csname zmatch\zcnt\endcsname\relax\gdef\ztest{T}\fi%
}%
\if F\ztest
\stepcounter{zcount}%
\expandafter\gdef\csname zmatch\thezcount\expandafter\endcsname
\expandafter{\z}%
\expandafter\ifx\expandafter\relax\the\mytoks\relax
\else\mytoks\expandafter{\the\mytoks,}\fi
\mytoks\expandafter{\the\expandafter\mytoks\z}%
\fi
}
The new list is \the\mytoks
\end{document}
Si no se siente cómodo con las listas de tokens, aquí tiene una versión que usa \def
s, en su lugar
\documentclass{article}
\usepackage{listofitems,pgffor}
\newcounter{zcount}
\expandafter\def\csname zmatch0\endcsname{-9999999}% NUMBER NOT IN LIST
\begin{document}
\def\zList{0,0,1,1,1,2,2,10}
The original list is \zList
\readlist\zdata{\zList}
\foreachitem\z\in\zdata[]{%
\gdef\ztest{F}%
\foreach\zcnt in {0,...,\thezcount}{%
\ifnum\z=\csname zmatch\zcnt\endcsname\relax\gdef\ztest{T}\fi%
}%
\if F\ztest
\stepcounter{zcount}%
\expandafter\xdef\csname zmatch\thezcount\expandafter\endcsname
\expandafter{\z}%
\ifnum\thezcount=1\relax
\xdef\zNewList{\csname zmatch1\endcsname}%
\else
\xdef\zNewList{\zNewList,\csname zmatch\thezcount\endcsname}
\fi
\fi
}
The new list is \zNewList
\end{document}
Respuesta3
Editar: Alguien pidió que se recortaran los ceros "innecesarios". Puedes hacer esto \FPclip
desde el paquete fp.
Si desea realizar la clasificación con las herramientas proporcionadas por tikz
, puede considerar la iteración anidada en la lista ya construida:
\documentclass[a4paper]{article}
\usepackage[nomessages]{fp}
\usepackage{tikz}
\usetikzlibrary{math}
% pgfmanual.pdf promises a ot of things to work which often don't due to bugs.
% E.g., with tikz/pgf 3.1.1 by default there is no \ifpgfmathfloatparseactive
% and evaluation of if-expressions via \pgfmathfloattofixed seems corrupted.
% \newif\ifpgfmathfloatparseactive
% \pgfmathfloatparseactivefalse
%
% Afaik current release (the date of writing this answer is
% August 28, 2020) is 3.1.5b.
% Seems things are fixed there.
\newcommand\PassFirstToSecond[2]{#2{#1}}%
\newif\ifalreadyinserted
\begin{document}
\newcommand\one{1}
\newcommand\two{2}
\newcommand\onecommaeight{1.8}
\newcommand*\zList{0,0,1,1,1.8,1.6754376,\one,\two,1,2,4+4,2,10,1.7,1.7,\onecommaeight,8,1.0}
\newcommand*\List{}% create List
\foreach \n in \zList {%
\pgfmathparse{\n}%
\let\n=\pgfmathresult
\FPclip{\n}{\n}%
\expandafter\PassFirstToSecond\expandafter{\List}{%
\def\List{}%
\global\alreadyinsertedfalse
\foreach \o in
}{%
\tikzmath{%
if (\o <= \n) then {{\xdef\List{\List\ifx\List\empty\else,\fi\o}};}%
else {{\xdef\List{\List\ifx\List\empty\else,\fi\ifalreadyinserted\else\n,\fi\o}};};%
if (\o >= \n) then {{\global\alreadyinsertedtrue};};%
}%
}%
\ifalreadyinserted\else
\xdef\List{\List\ifx\List\empty\else,\fi\n}%
\fi
}
Show Zero List: \texttt{\frenchspacing\string\zList: \meaning\zList}
Show List-Actual: \texttt{\frenchspacing\string\List: \meaning\List}
\end{document}
Explicación:
Tienes el⟨lista pasada por el usuario⟩( \zList
) y el⟨lista ordenada creada hasta ahora⟩( \List
).
Con cada elemento \n
del⟨lista pasada por el usuario⟩Haz lo siguiente:
Establezca una bandera (
\ifalreadyinserted
/\alreadyinsertedfalse
/\alreadyinsertedtrue
) para indicar que podría ser necesario insertar ese elemento\n
en el⟨lista ordenada creada hasta ahora⟩."Mira" cada elemento
\o
del⟨lista ordenada creada hasta ahora⟩para saber si el elemento\n
de la⟨lista pasada por el usuario⟩necesita ser insertado en el⟨lista ordenada creada hasta ahora⟩:Mientras el valor del elemento
\o
de la⟨lista ordenada creada hasta ahora⟩no es mayor que el valor del elemento\n
de la⟨lista pasada por el usuario⟩, el elemento\n
de la⟨lista pasada por el usuario⟩no es necesario insertarlo en el⟨lista ordenada creada hasta ahora⟩.Si, mientras la bandera todavía indica que podría ser necesario insertar el elemento
\n
en el⟨lista ordenada creada hasta ahora⟩, ocurre la primera vezque el valor del elemento\o
de la⟨lista ordenada creada hasta ahora⟩es mayor que el valor del elemento\n
de la⟨lista pasada por el usuario⟩, el elemento\n
de la⟨lista pasada por el usuario⟩necesita ser insertado en el⟨lista ordenada creada hasta ahora⟩antes del elemento\o
de la⟨lista ordenada creada hasta ahora⟩.
Si ocurre la primera vez...—la bandera es necesaria para saber si es la primera vez.Si el valor del elemento
\o
de la⟨lista ordenada creada hasta ahora⟩es mayor o igual al valor del elemento\n
de la⟨lista pasada por el usuario⟩, entonces es necesario establecer la bandera para indicar que no es necesario insertar el elemento\n
del⟨lista pasada por el usuario⟩en el⟨lista pasada por el usuario⟩.Si después de observar todos los elementos
\o
del⟨lista ordenada creada hasta ahora⟩la bandera todavía indica que podría ser necesario insertar el elemento\n
del⟨lista pasada por el usuario⟩en el⟨lista ordenada creada hasta ahora⟩, entonces esto indica que el valor del elemento\n
del⟨lista pasada por el usuario⟩es mayor que los valores de todos los elementos\o
que ya están en el⟨lista ordenada creada hasta ahora⟩y que por lo tanto el elemento\n
de la⟨lista pasada por el usuario⟩debe anexarse al⟨lista ordenada creada hasta ahora⟩.
Respuesta4
En aras de la variedad, aquí tienes una solución basada en LuaLaTeX.
La cadena de entrada, definida por, digamos, \zList
puede contener números, macros (excepto \zList
ella misma) que se expanden a números y cadenas que contienen una lista de números separados por comas. No es necesario ordenar los números en orden ascendente.
\unique
extrae los números ordenados únicos contenidos en \zList
.
% !TeX program = lualatex
\documentclass{article}
%% Lua-side code
\usepackage{luacode} % for 'luacode' environment
\begin{luacode}
function string_to_table (str)
local fields = {} -- initialize the table
str:gsub( "([^,]*)" , function ( c )
-- strip off anyleading and trailing whitespace:
c = c:gsub ( "^%s*(.-)%s*$" , "%1" )
-- insert 'c' in 'fields'
table.insert ( fields , tonumber(c) )
end )
return fields
end
function remove_duplicate_entries ( t )
-- from https://stackoverflow.com/a/20067270/1014365
local hash = {}
local res = {}
for _,v in ipairs(t) do
if (not hash[v]) then
res[#res+1] = v
hash[v] = true
end
end
return res
end
function unique ( s )
local t
-- Convert string 's' to a Lua table:
t = string_to_table ( s )
-- Sort the table entries in ascending order:
table.sort ( t , function(a,b) return a<b end)
-- Retain the unique elements:
t = remove_duplicate_entries ( t )
-- Convert table back to string and print:
tex.sprint ( table.concat ( t, "," ) )
end
\end{luacode}
%% LaTeX-side code:
\newcommand\unique[1]{\directlua{unique(\luastring{#1})}}
\begin{document}
\def\mynum{10}
\newcommand\mystr{"\mynum,0"}
\def\zList{0,10,1,1,2,2,1,0,\mynum,\mystr}
\zList
\unique{\zList}
\end{document}