Извлечь список различных значений из другого списка (удалить дубликаты)

Извлечь список различных значений из другого списка (удалить дубликаты)

Предположим, у меня есть список, определенный с помощью \def\zList{0,0,1,1,1,2,2,10}.

Как получить список (разделенный запятыми) 0,1,2,10, содержащий уникальные/различимые значения 0,0,1,1,1,2,2,10?

Может быть, есть способ pgfmath?(Мне также нужно все это в более сложных контекстах [pgfplots/-table], так что pgfmathбыло бы не так уж и плохо...)

введите описание изображения здесь

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

решение1

Вы можете удалить дубликаты, используяexpl3

\ExplSyntaxOn
\cs_new_eq:NN \removeclistdupes \clist_remove_duplicates:N
\ExplSyntaxOff
\Removeclistdupes\List

Полный пример

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

решение2

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

введите описание изображения здесь

Если вам некомфортно работать со списками токенов, то вот версия, которая использует \defs вместо

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

решение3

Редактировать: Кто-то просил обрезать "ненужные" нули. Это можно сделать \FPclipиз пакета fp.

Если вы хотите выполнить сортировку с помощью инструментов, предоставляемых tikz, вы можете рассмотреть вложенную итерацию по уже сформированному списку:

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

введите описание изображения здесь

Объяснение:

У тебя есть⟨список, переданный пользователем⟩( \zList) и⟨сортированный список, созданный на данный момент⟩( \List).

С каждым \nэлементом⟨список, переданный пользователем⟩сделайте следующее:

  • Установите флаг ( \ifalreadyinserted// ) \alreadyinsertedfalse, \alreadyinsertedtrueчтобы указать, что может возникнуть необходимость вставить этот элемент \nв⟨сортированный список, созданный на данный момент⟩.

  • «Посмотрите» на каждый \oэлемент⟨сортированный список, созданный на данный момент⟩для выяснения, является ли элемент\n элемент⟨список, переданный пользователем⟩необходимо вставить в⟨сортированный список, созданный на данный момент⟩:

    Пока значение \oэлемента⟨сортированный список, созданный на данный момент⟩не больше значения \nэлемента⟨список, переданный пользователем⟩\n, элемент⟨список, переданный пользователем⟩не нужно вставлять в⟨сортированный список, созданный на данный момент⟩.

    Если, пока флаг все еще указывает на то, что может возникнуть необходимость вставить элемент \nв⟨сортированный список, созданный на данный момент⟩, это происходит в первый разчто значение \oэлемента⟨сортированный список, созданный на данный момент⟩больше, чем значение \nэлемента⟨список, переданный пользователем⟩\n, элемент⟨список, переданный пользователем⟩необходимо вставить в⟨сортированный список, созданный на данный момент⟩\oперед элементом⟨сортированный список, созданный на данный момент⟩.
    Если это происходит в первый раз... — флаг нужен для того, чтобы узнать, происходит ли это в первый раз.

    Если значение \oэлемента⟨сортированный список, созданный на данный момент⟩больше или равно значению \nэлемента⟨список, переданный пользователем⟩, то необходимо установить флаг, указывающий на то, что нет необходимости вставлять \nэлемент⟨список, переданный пользователем⟩в⟨список, переданный пользователем⟩.

  • Если после рассмотрения всех \oэлементов⟨сортированный список, созданный на данный момент⟩флаг по-прежнему указывает на то, что может возникнуть необходимость вставить \nэлемент⟨список, переданный пользователем⟩в⟨сортированный список, созданный на данный момент⟩, то это указывает на то, что значение \nэлемента⟨список, переданный пользователем⟩больше, чем значения всех элементов \o, которые уже находятся в⟨сортированный список, созданный на данный момент⟩\nи что поэтому элемент⟨список, переданный пользователем⟩необходимо приложить к⟨сортированный список, созданный на данный момент⟩.

решение4

Для разнообразия приведу решение на основе LuaLaTeX.

Входная строка -- определяемая, скажем, \zList-- может содержать числа, макросы (кроме \zListсебя), которые расширяются до чисел, и строки, содержащие список чисел, разделенных запятыми. Числа не обязательно должны быть отсортированы в порядке возрастания.

\uniqueизвлекает уникальные отсортированные числа, содержащиеся в \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}

Связанный контент