TransWikia.com

Extract a list of distinct values from another list (remove dupes)

TeX - LaTeX Asked on January 27, 2021

Suppose I have a list defined via defzList{0,0,1,1,1,2,2,10}.

How can I obtain a (comma-separated) list 0,1,2,10 that contains the unique/distinct values in 0,0,1,1,1,2,2,10?

Is there perhaps a way with pgfmath? (I also need the whole thing in more complex contexts [pgfplots/-table], so pgfmath wouldn’t be so bad …)

enter image description here

documentclass[a4paper]{article}
usepackage{tikz}
begin{document}
defzList{0,0,1,1,1,2,2,10}

letList=empty% create List
foreach n  in zList {%
pgfmathparse{n}%  <--- A clever method needed here
  ifxemptyList{} xdefList{pgfmathresult}%
  else xdefList{List,pgfmathresult}%
  fi}
  
Show Zero List: zList

Show List-Actual: List

Show List-Target: 0,1,2,10
end{document}

4 Answers

You could remove duplicates using expl3

ExplSyntaxOn
cs_new_eq:NN removeclistdupes clist_remove_duplicates:N
ExplSyntaxOff
RemoveclistdupesList

A full example

documentclass[a4paper]{article}
usepackage{expl3}
ExplSyntaxOn
cs_new_eq:NN removeclistdupes clist_remove_duplicates:N
ExplSyntaxOff
usepackage{tikz}

begin{document}
defzList{0,0,1,1,1,2,2,10}

letList=empty% create List
zList
foreach n  in zList {%
pgfmathparse{n}%  <--- A clever method needed here
  ifxemptyList{} xdefList{pgfmathresult}%
  else xdefList{List,pgfmathresult}%
  fi}
removeclistdupesList
  
Show Zero List: zList

Show List-Actual: List

Show List-Target: 0,1,2,10
end{document}

Correct answer by Joseph Wright on January 27, 2021

Edit: Someone asked to have "unnecessary" zeros clipped. You can do this with FPclip from the fp-package.

If you wish to do the sorting-out with the tools provided by tikz, you can consider nested iteration on the already constructed list:

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.
% newififpgfmathfloatparseactive
% pgfmathfloatparseactivefalse
%
% Afaik current release (the date of writing this answer is 
% August 28, 2020) is 3.1.5b.
% Seems things are fixed there.

newcommandPassFirstToSecond[2]{#2{#1}}%
newififalreadyinserted

begin{document}

newcommandone{1}
newcommandtwo{2}
newcommandonecommaeight{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}%
    letn=pgfmathresult
    FPclip{n}{n}%
    expandafterPassFirstToSecondexpandafter{List}{%
      defList{}%
      globalalreadyinsertedfalse
      foreach o in 
    }{%
      tikzmath{%
        if (o <= n) then {{xdefList{ListifxListemptyelse,fio}};}%
                      else {{xdefList{ListifxListemptyelse,fiifalreadyinsertedelsen,fio}};};%
        if (o >= n) then {{globalalreadyinsertedtrue};};%
      }%
    }%
    ifalreadyinsertedelse
      xdefList{ListifxListemptyelse,fin}%
    fi
}
  
Show Zero List: texttt{frenchspacingstringzList: meaningzList}

Show List-Actual: texttt{frenchspacingstringList: meaningList}

end{document}

enter image description here

Explanation:

You have the ⟨list passed by the user⟩ (zList) and the ⟨sorted list created so far⟩ (List).

With each element n of the ⟨list passed by the user⟩ do the following:

  • Set a flag (ifalreadyinserted/alreadyinsertedfalse/alreadyinsertedtrue) to indicate that there might be need to insert that element n into the ⟨sorted list created so far⟩.

  • "Look" at each element o of the ⟨sorted list created so far⟩ for finding out if the element n of the ⟨list passed by the user⟩ needs to be inserted into the ⟨sorted list created so far⟩ :

    As long as the value of the element o of the ⟨sorted list created so far⟩ is not greater than the value of the element n of the ⟨list passed by the user⟩, the element n of the ⟨list passed by the user⟩ does not need to be inserted into the ⟨sorted list created so far⟩.

    If, while the flag still indicates that there might be need to insert the element n into the ⟨sorted list created so far⟩, it occurs the first time that the value of the element o of the ⟨sorted list created so far⟩ is greater than the value of the element n of the ⟨list passed by the user⟩, the element n of the ⟨list passed by the user⟩ needs to be inserted into the ⟨sorted list created so far⟩ before the element o of the ⟨sorted list created so far⟩.
    If it occurs the first time...—the flag is needed for finding out if it is the first time.

    If the value of the element o of the ⟨sorted list created so far⟩ is greater than or equal to the value of the element n of the ⟨list passed by the user⟩, then the flag needs to be set to indicate that there is no need to insert the element n of the ⟨list passed by the user⟩ into the ⟨list passed by the user⟩.

  • If after looking at all elements o of the ⟨sorted list created so far⟩ the flag still indicates that there might be need to insert the element n of the ⟨list passed by the user⟩ into the ⟨sorted list created so far⟩, then this indicates that the value of the element n of the ⟨list passed by the user⟩ is greater than the values of all elements o that are already in the ⟨sorted list created so far⟩ and that therefore the element n of the ⟨list passed by the user⟩ needs to be appended to the ⟨sorted list created so far⟩.

Answered by Ulrich Diez on January 27, 2021

documentclass{article}
usepackage{listofitems,pgffor}
newcounter{zcount}
newtoksmytoks
mytoks{}
expandafterdefcsname zmatch0endcsname{-9999999}% NUMBER NOT IN LIST
begin{document}
defzList{0,0,1,1,1,2,2,10}
The original list is zList

readlistzdata{zList}
foreachitemzinzdata[]{%
  gdefztest{F}%
  foreachzcnt in {0,...,thezcount}{%
    ifnumz=csname zmatchzcntendcsnamerelaxgdefztest{T}fi%
  }%
  if Fztest
    stepcounter{zcount}%
    expandaftergdefcsname zmatchthezcountexpandafterendcsname
      expandafter{z}%
    expandafterifxexpandafterrelaxthemytoksrelax
      elsemytoksexpandafter{themytoks,}fi
    mytoksexpandafter{theexpandaftermytoksz}%
  fi
}
The new list is themytoks
end{document}

enter image description here

If you are not comfortable with token lists, then here is a version that uses defs, instead

documentclass{article}
usepackage{listofitems,pgffor}
newcounter{zcount}
expandafterdefcsname zmatch0endcsname{-9999999}% NUMBER NOT IN LIST
begin{document}
defzList{0,0,1,1,1,2,2,10}
The original list is zList

readlistzdata{zList}
foreachitemzinzdata[]{%
  gdefztest{F}%
  foreachzcnt in {0,...,thezcount}{%
    ifnumz=csname zmatchzcntendcsnamerelaxgdefztest{T}fi%
  }%
  if Fztest
    stepcounter{zcount}%
    expandafterxdefcsname zmatchthezcountexpandafterendcsname
      expandafter{z}%
    ifnumthezcount=1relax
      xdefzNewList{csname zmatch1endcsname}%
    else
      xdefzNewList{zNewList,csname zmatchthezcountendcsname}
    fi
  fi
}

The new list is zNewList
end{document}

Answered by Steven B. Segletes on January 27, 2021

For the sake of variety, here's a LuaLaTeX-based solution.

The input string -- defined by, say, zList -- may contain numbers, macros (except zList itself) that expand to numbers, and strings that contain a list of comma-separated numbers. The numbers needn't be sorted in ascending order.

unique extracts the unique sorted numbers contained in zList.

enter image description here

% !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:
newcommandunique[1]{directlua{unique(luastring{#1})}}

begin{document}
defmynum{10}
newcommandmystr{"mynum,0"}
defzList{0,10,1,1,2,2,1,0,mynum,mystr}

zList

unique{zList}
end{document}

Answered by Mico on January 27, 2021

Add your own answers!

Ask a Question

Get help from others!

© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP