2

Follow this question

I have error when I look for the missing value :

* t1
  #+tblname: t1
  |----+---|
  | P1 | 1 |
  | P2 | 2 |
  | P3 | 3 |
  |----+---+

* t2
  #+tblname: t2
  |----+---|
  | P1 | 10 |
  | P2 | 20 |
  | P3 | 30 |
  |----+---+

* t3
  #+tblname: t3
  |----+-----|
  | P1 | 100 |
  | P2 | 200 |
  | P3 | 300 |
  | P4 |     |
  |----+-----|


* Total
  | Nom |        |
  |-----+--------|
  | P1  |    111 |
  | P2  |    222 |
  | P3  |    333 |
  | P4  | #ERROR |
  #+TBLFM: $2='(my/total-for $1)


* Code
#+begin_src elisp
  (defun my/list-of-tables ()
    (org-element-map (org-element-parse-buffer) 
        'table
      (lambda (tbl) (plist-get (cadr tbl) :name))))

#+end_src

#+RESULTS:
: my/list-of-tables

#+begin_src elisp :results verbatim
  (my/list-of-tables)
  
#+end_src

#+RESULTS:
: ("t1")

#+begin_src elisp
  (defun my/total-for (name)
    (let ((tables (my/list-of-tables))
          (sum 0))
      (dolist (tab tables)
        (setq sum (+ sum (string-to-number
                          (org-lookup-first name
                                            (org-table-get-remote-range tab "@I$1..@II$1")
                                            (org-table-get-remote-range tab "@I$2..@II$2"))))))
      (number-to-string sum)))
#+end_src

#+RESULTS:
: my/total-for

1 Answer 1

2

The problem here is that a nil is passed to string-to-number and that barfs when given a non-string as argument. You can check by evaluating this:

#+begin_src elisp :results verbatim
(org-lookup-first "P4" (org-table-get-remote-range "t3"  "@I$1..@II$1")
                       (org-table-get-remote-range "t3"  "@I$2..@II$2"))
#+end_src

#+RESULTS:
: nil

and

#+begin_src elisp
(string-to-number nil)
#+end_src

which generates an error.

You can do one of the following:

  • check the return value of org-lookup-first and only call string-to-number on it when that is not nil. What to do when that is nil is up to you. The simplest thing to do is to pretend that it is a 0.

  • instead of using string-to-number, you can write your own function string-to-number-safe that checks the type of its argument and returns 0 if not a string; otherwise it calls string-to-number and returns whatever that returns.

  • you can leave everything as is, but fill the P4 value in the t3 table with some non-empty string that will cause string-to-number to return 0: anything that looks non-numeric will do that, e.g. the string "N/A".

  • you can let it generate an error (the current behavior).

  • error handling opens up many cans of worms, so you can add much more complicated methods of dealing with such problems. The only question is what you want to catch and how much complication you can afford.

If you want any non-numeric value to act as a 0, I would probably choose option 3: it does not require any changes to the code and you only have to add a string like N/A as the value.

If you absolutely want an empty value, I would probably choose option 2. The code is simple:

(defun string-to-number-safe (s)
   (if (stringp s)
       (string-to-number s)
     0)

You can then replace the string-to-number call in the definition of my/total-for with string-to-number-safe.

If you want something more complicated, you are on your own :)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.