From mboxrd@z Thu Jan 1 00:00:00 1970 From: Jarmo Hurri Subject: [Patch] Table lookup functions: director's cut Date: Mon, 15 Oct 2012 10:25:59 +0300 Message-ID: <87vcecrybc.fsf@syk.fi> Mime-Version: 1.0 Content-Type: multipart/mixed; boundary="=-=-=" Return-path: Received: from eggs.gnu.org ([208.118.235.92]:42055) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1TNf44-0003K5-7V for emacs-orgmode@gnu.org; Mon, 15 Oct 2012 03:26:20 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1TNf42-0006qr-6x for emacs-orgmode@gnu.org; Mon, 15 Oct 2012 03:26:16 -0400 Received: from plane.gmane.org ([80.91.229.3]:46698) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1TNf41-0006qc-SX for emacs-orgmode@gnu.org; Mon, 15 Oct 2012 03:26:14 -0400 Received: from list by plane.gmane.org with local (Exim 4.69) (envelope-from ) id 1TNf46-0000SB-HW for emacs-orgmode@gnu.org; Mon, 15 Oct 2012 09:26:18 +0200 Received: from cs181237063.pp.htv.fi ([82.181.237.63]) by main.gmane.org with esmtp (Gmexim 0.1 (Debian)) id 1AlnuQ-0007hv-00 for ; Mon, 15 Oct 2012 09:26:18 +0200 Received: from jarmo.hurri by cs181237063.pp.htv.fi with local (Gmexim 0.1 (Debian)) id 1AlnuQ-0007hv-00 for ; Mon, 15 Oct 2012 09:26:18 +0200 List-Id: "General discussions about Org-mode." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: emacs-orgmode-bounces+geo-emacs-orgmode=m.gmane.org@gnu.org Sender: emacs-orgmode-bounces+geo-emacs-orgmode=m.gmane.org@gnu.org To: emacs-orgmode@gnu.org --=-=-= Greetings. Here is the newest version of the patch implementing table lookup functions. First, to see what these little functions can do, take a look at http://orgmode.org/worg/org-tutorials/org-lookups.html Please note that the patch is not in the official git repository yet, so you can not replicate the examples in the tutorial. When and if I get the word that the patch has been applied, I will add a link to the tutorial in the main tutorial page on Worg. A few notes on the patch: 1. There is now a third lookup function org-lookup-all, which I thought would be a very useful addition. 2. The three lookup functions are still defined by calls to a single macro. Heck, one of the advertised reasons for using Lisp are its macro capabilities, so I could not resist. The generated documentation strings now contain a reference to the macro, so users can locate the macro in org-table.el. 3. CL is no longer used in the implementation. I decided to implement the search using a while control structure. I wanted to do a tail recursive implementation, but then found out that Emacs Lisp does not do tail-call optimization. If you'd like me to use a different control structure (for some reason), I can change it. 4. Technically R-LIST is now an optional parameter in the lookup functions, because if it is nil, the matching value from S-LIST is returned directly. I decided not to define it to be an optional parameter, because that would simply look weird to a first-time user: the default use would then be to find a value and return the same value, which would not make much sense. Have fun! Jarmo --=-=-= Content-Type: text/x-patch Content-Disposition: attachment; filename=0001-Table-lookup-functions.patch Content-Description: table lookup functions >From dfa552f2e8b61ce301900dcce7da92d4f8f0854a Mon Sep 17 00:00:00 2001 From: Jarmo Hurri Date: Mon, 15 Oct 2012 09:54:24 +0300 Subject: [PATCH] Table lookup functions * lisp/org-table.el: added macro org-define-lookup-function and the calls to this macro that generate the lookup functions org-lookup-first, org-lookup-last and org-lookup-all * doc/org.texi: documented lookup functions --- doc/org.texi | 50 ++++++++++++++++++++++++++++++++++++++++++++++++-- lisp/org-table.el | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 80 insertions(+), 2 deletions(-) diff --git a/doc/org.texi b/doc/org.texi index c8f0afb..6d8f59a 100644 --- a/doc/org.texi +++ b/doc/org.texi @@ -378,6 +378,7 @@ The spreadsheet * Durations and time values:: How to compute durations and time values * Field and range formulas:: Formula for specific (ranges of) fields * Column formulas:: Formulas valid for an entire column +* Lookup functions:: Lookup functions for searching tables * Editing and debugging formulas:: Fixing formulas * Updating the table:: Recomputing all dependent fields * Advanced features:: Field and column names, parameters and automatic recalc @@ -2397,6 +2398,7 @@ formula, moving these references by arrow keys * Durations and time values:: How to compute durations and time values * Field and range formulas:: Formula for specific (ranges of) fields * Column formulas:: Formulas valid for an entire column +* Lookup functions:: Lookup functions for searching tables * Editing and debugging formulas:: Fixing formulas * Updating the table:: Recomputing all dependent fields * Advanced features:: Field and column names, parameters and automatic recalc @@ -2782,7 +2784,7 @@ can also be used to assign a formula to some but not all fields in a row. Named field, see @ref{Advanced features}. @end table -@node Column formulas, Editing and debugging formulas, Field and range formulas, The spreadsheet +@node Column formulas, Lookup functions, Field and range formulas, The spreadsheet @subsection Column formulas @cindex column formula @cindex formula, for table column @@ -2821,7 +2823,51 @@ stores it. With a numeric prefix argument(e.g.@: @kbd{C-5 C-c =}) the command will apply it to that many consecutive fields in the current column. @end table -@node Editing and debugging formulas, Updating the table, Column formulas, The spreadsheet +@node Lookup functions, Editing and debugging formulas, Column formulas, The spreadsheet +@subsection Lookup functions +@cindex lookup functions in tables +@cindex table lookup functions + +Org has three predefined Emacs Lisp functions for lookups in tables. +@table @code +@item (org-lookup-first VAL S-LIST R-LIST &optional PREDICATE) +@findex org-lookup-first +Searches for the first element @code{S} in list @code{S-LIST} for which +@lisp +(PREDICATE VAL S) +@end lisp +is @code{t}; returns the value from the corresponding position in list +@code{R-LIST}. The default @code{PREDICATE} is @code{equal}. Note that the +parameters @code{VAL} and @code{S} are passed to @code{PREDICATE} in the same +order as the correspoding parameters are in the call to +@code{org-lookup-first}, where @code{VAL} precedes @code{S-LIST}. If +@code{R-LIST} is @code{nil}, the matching element @code{S} of @code{S-LIST} +is returned. +@item (org-lookup-last VAL S-LIST R-LIST &optional PREDICATE) +@findex org-lookup-last +Similar to @code{org-lookup-first} above, but searches for the @i{last} +element for which @code{PREDICATE} is @code{t}. +@item (org-lookup-all VAL S-LIST R-LIST &optional PREDICATE) +@findex org-lookup-all +Similar to @code{org-lookup-first}, but searches for @i{all} elements for +which @code{PREDICATE} is @code{t}, and returns @i{all} corresponding +values. This function can not be used by itself in a formula, because it +returns a list of values. However, powerful lookups can be built when this +function is combined with other Emacs Lisp functions. +@end table + +If the ranges used in these functions contain empty fields, the @code{E} mode +for the formula should usually be specified: otherwise empty fields will not be +included in @code{S-LIST} and/or @code{R-LIST} which can, for example, result +in an incorrect mapping from an element of @code{S-LIST} to the corresponding +element of @code{R-LIST}. + +These three functions can be used to implement associative arrays, count +matching cells, rank results, group data etc. For practical examples +see @uref{http://orgmode.org/worg/org-tutorials/org-lookups.html, this +tutorial on Worg}. + +@node Editing and debugging formulas, Updating the table, Lookup functions, The spreadsheet @subsection Editing and debugging formulas @cindex formula editing @cindex editing, of table formulas diff --git a/lisp/org-table.el b/lisp/org-table.el index 0555041..33fcb41 100644 --- a/lisp/org-table.el +++ b/lisp/org-table.el @@ -4875,6 +4875,38 @@ list of the fields in the rectangle ." (org-table-get-range (match-string 0 form) tbeg 1)) form))))))))) +(defmacro org-define-lookup-function (mode) + (let ((mode-str (symbol-name mode)) + (first-p (equal mode 'first)) + (all-p (equal mode 'all))) + (let ((plural-str (if all-p "s" ""))) + `(defun ,(intern (format "org-lookup-%s" mode-str)) (val s-list r-list &optional predicate) + ,(format "Find %s occurrence%s of VAL in S-LIST; return corresponding element%s of R-LIST. +If R-LIST is nil, return matching element%s of S-LIST. +If PREDICATE is not nil, use it instead of `equal' to match VAL. +Matching is done by (PREDICATE VAL S), where S is an element of S-LIST. +This function is generated by a call to the macro `org-define-lookup-function'." + mode-str plural-str plural-str plural-str) + (let ,(let ((lvars '((p (or predicate 'equal)) + (sl s-list) + (rl (or r-list s-list)) + (ret nil)))) + (if first-p (add-to-list 'lvars '(match-p nil))) + lvars) + (while ,(if first-p '(and (not match-p) sl) 'sl) + (progn + (if (funcall p val (car sl)) + (progn + ,(if first-p '(setq match-p t)) + (let ((rval (car rl))) + (setq ret ,(if all-p '(append ret (list rval)) 'rval))))) + (setq sl (cdr sl) rl (cdr rl)))) + ret))))) + +(org-define-lookup-function first) +(org-define-lookup-function last) +(org-define-lookup-function all) + (provide 'org-table) ;; Local variables: -- 1.7.7.6 --=-=-=--