6

Suppose I have a table with say two columns. How can I sort its rows based on the first column? Is it possible in latex? If I do it in another application (like MS Excel) can I import the data from my worksheet into a tabular?
I would be very happy if you suggest me a way to handle this sorting, the easier the better! BTW I looked at sorting alphanumeric listing and didn't get it at all!

3 Answers 3

3

I have no doubt that datatool is the way to go here. However, it is possible to use the code from my answer to the sorting question to do this.

You need to define a new comparator that compares only the first column. This particular implementation wants something that has a top level expansion into a row. E.g., you can define a macro for each row.

\def\rowi  {C & foo & qwer \\}
\def\rowii {A & bar & asdf \\}
\def\rowiii{B & baz & zxcv \\}

The goal is to be able to write the following to get sorted rows.

\begin{tabular}{lll}
\toprule
Letters & Metasyntatic variables & Gibberish\\
\midrule
\sortfirstcol{\rowi\rowii\rowiii}
\bottomrule
\end{tabular}

In particular, the \sortfirstcol{\rowi\rowii\rowiii} should expand to the sorted rows. As with \sortalpha, \sortfirstcol is just a simple macro that expands \sort with the comparator \sort@firstcol as its first argument.

\newcommand*\sortfirstcol{\sort\sort@firstcol} 

The comparator isn't extremely complicated, but it does need to extract the first column of each argument. For that, we use a helper macro. And in keeping with the spirit of the original answer, this version is expandable.

\def\sort@firstcol#1#2{%
    \expandafter\expandafter\expandafter
        \sort@fc@helper\expandafter#1#2#1#2%
}

\def\sort@fc@helper#1&#2\\#3&#4\\#5#6#7{%
    \ifnumcomp{\pdfstrcmp{#1}{#3}}<{0}
    {#7{#5}{#6}}% else
    {#7{#6}{#5}}%
}

The \expandafters are going to expand the first two arguments a single time. For example, if the comparator is "called" as \sort@firstcol{\rowi}{\rowii}{...}, then it will expand to

\sort@fc@helper C & foo & qwer \\A & bar & asdf \\\rowi\rowii{...}

and so #1 will be the tokens "C ", #3 will be "A ", #5 will be \rowi, #6 will be \rowii, and lastly #7 will be the {...} that is the continuation that gets passed to the comparator.

From here, it's identical to \sort@alpha: it compares #1 and #3, passing #5 and #6 to the continuation.

Lastly, due to the representation of the rows, the \sortend and \sortoutput macros should be set to just expand to their argument:

\newcommand*\sortoutput[1]{#1}
\newcommand*\sortend[1]{#1}

The following is a complete example using the above code and the code from the previous answer.

\documentclass{article}
\usepackage{etoolbox}
\usepackage{booktabs}
\makeatletter
% #1 - comparator
% #2 - token list to sort
\newcommand\sort[2]{%
        \ifstrempty{#2}
        {}% else
        {%
                \sort@begin#1{}#2\sort@s\sort@begin
        }%
}

% helpers
\def\sort@s{\sort@s}
\def\ifsort@s#1{%
        \ifx\sort@s#1%
                \expandafter\@firstoftwo
        \else
                \expandafter\@secondoftwo
        \fi
}

% #1 - comparator
% #2 - tokens processed so far
% #3 - smallest token so far
% #4 - rest of the list
\def\sort@begin#1#2#3#4\sort@begin{%
        \ifsort@s{#4}
        {%
                \sortend{#3}%
                \sort#1{#2}%
        }% else
        {%
                \sort@go#1{#2}{#3}#4\sort@go
        }%
}

%     #1 - comparator
% #2 - tokens processed so far
% #3 - smallest token so far
% #4 - token under consideration
% #5 - rest of the list
\def\sort@go#1#2#3#4#5\sort@go{%
        #1{#3}{#4}{\sort@output#1{#2}{#5}}%
}
% #1 - comparator
% #2 - tokens processed so far
% #3 - rest of the list
% #4 - smaller of the two tokens
% #5 - larger of the two tokens
\def\sort@output#1#2#3#4#5{%
        \ifsort@s{#3}
        {%
                \sortoutput{#4}%
                \sort#1{#2{#5}}%
        }% else
        {%
                \sort@begin#1{#2{#5}}{#4}#3\sort@begin
        }%
}

\def\sort@numlt#1#2#3{%
        \ifnumcomp{#1}<{#2}
        {#3{#1}{#2}}% else
        {#3{#2}{#1}}%
}

\def\sort@numgt#1#2#3{%
        \ifnumcomp{#1}>{#2}
        {#3{#1}{#2}}% else
        {#3{#2}{#1}}%
}

\def\sort@alpha#1#2#3{%
        \ifnumcomp{\pdfstrcmp{#1}{#2}}<{0}
        {#3{#1}{#2}}% else
        {#3{#2}{#1}}%
}

\def\sort@firstcol#1#2{%
    \expandafter\expandafter\expandafter
        \sort@fc@helper\expandafter#1#2#1#2%
}

\def\sort@fc@helper#1&#2\\#3&#4\\#5#6#7{%
    \ifnumcomp{\pdfstrcmp{#1}{#3}}<{0}
    {#7{#5}{#6}}% else
    {#7{#6}{#5}}%
}

\newcommand*\sortnumlt{\sort\sort@numlt}
\newcommand*\sortnumgt{\sort\sort@numgt}
\newcommand*\sortalpha{\sort\sort@alpha}
\newcommand*\sortfirstcol{\sort\sort@firstcol}
\makeatother

% Change these to change out the sort outputs.
\newcommand*\sortoutput[1]{#1}
\newcommand*\sortend[1]{#1}

\begin{document}

\def\rowi  {C & foo & qwer \\}
\def\rowii {A & bar & asdf \\}
\def\rowiii{B & baz & zxcv \\}

\begin{tabular}{lll}
\toprule
Letters & Metasyntatic variables & Gibberish\\
\midrule
\sortfirstcol{\rowi\rowii\rowiii}
\bottomrule
\end{tabular}

\end{document}

Finally, I should point out that this representation (that is, rows as macros) isn't necessary. Due to the way \sort processes its arguments, one could easily write a comparator that allowed one to write the following.

\sortfirstcol{%
    {C & foo & qwer}%
    {A & bar & asdf}%
    {B & baz & zxcv}%
}
\def\sortoutput#1{#1\\}
\def\sortend#1{#1\\}

Perhaps I should have done that. The comparator would be slightly simpler.

Also, as implemented, a space at the beginning or end of the first column matters. One could probably deal with that too.

4

The datatool package has quite a lot of functions to sort and analyse data.

1

There are a number of good techniques for transferring Excel or other spreadsheet data into latex source. See this answer here. If you are using a TeXShop on a Mac, there is also a built-in "Paste Spreadsheet Cells" macro that will do the trick.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .