Extraer una lista de valores distintos de otra lista (eliminar duplicados)

Extraer una lista de valores distintos de otra lista (eliminar duplicados)

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,10que 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 pgfmathno sería tan malo...)

ingrese la descripción de la imagen aquí

\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}

ingrese la descripción de la imagen aquí

Si no se siente cómodo con las listas de tokens, aquí tiene una versión que usa \defs, 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 \FPclipdesde 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}

ingrese la descripción de la imagen aquí

Explicación:

Tienes el⟨lista pasada por el usuario⟩( \zList) y el⟨lista ordenada creada hasta ahora⟩( \List).

Con cada elemento \ndel⟨lista pasada por el usuario⟩Haz lo siguiente:

  • Establezca una bandera ( \ifalreadyinserted/ \alreadyinsertedfalse/ \alreadyinsertedtrue) para indicar que podría ser necesario insertar ese elemento \nen el⟨lista ordenada creada hasta ahora⟩.

  • "Mira" cada elemento \odel⟨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 \ode la⟨lista ordenada creada hasta ahora⟩no es mayor que el valor del elemento \nde la⟨lista pasada por el usuario⟩, el elemento \nde 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 \nen el⟨lista ordenada creada hasta ahora⟩, ocurre la primera vezque el valor del elemento \ode la⟨lista ordenada creada hasta ahora⟩es mayor que el valor del elemento \nde la⟨lista pasada por el usuario⟩, el elemento \nde la⟨lista pasada por el usuario⟩necesita ser insertado en el⟨lista ordenada creada hasta ahora⟩antes del elemento \ode 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 \ode la⟨lista ordenada creada hasta ahora⟩es mayor o igual al valor del elemento \nde la⟨lista pasada por el usuario⟩, entonces es necesario establecer la bandera para indicar que no es necesario insertar el elemento \ndel⟨lista pasada por el usuario⟩en el⟨lista pasada por el usuario⟩.

  • Si después de observar todos los elementos \odel⟨lista ordenada creada hasta ahora⟩la bandera todavía indica que podría ser necesario insertar el elemento \ndel⟨lista pasada por el usuario⟩en el⟨lista ordenada creada hasta ahora⟩, entonces esto indica que el valor del elemento \ndel⟨lista pasada por el usuario⟩es mayor que los valores de todos los elementos \oque ya están en el⟨lista ordenada creada hasta ahora⟩y que por lo tanto el elemento \nde 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, \zListpuede contener números, macros (excepto \zListella 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.

\uniqueextrae los números ordenados únicos contenidos en \zList.

ingrese la descripción de la imagen aquí

% !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}

información relacionada