8

How can I sum a set of numbers interspersed with text in the region (not a rectangular region). For example, if the region contains this text:

Widgets 234
Sprockets 44
Nubbins 12
Fork handles 4
4 Candles

I'm looking for a command that will report 298 somehow (298 = 234 + 44 + 12 + 4 + 4) -- I guess either by echoing that to the message area or inserting it in the buffer.

Ideally it would cope with both integers and floating point numbers.

Croad Langshan
  • 3,192
  • 14
  • 42

5 Answers5

6

Here's one:

(require 'cl-lib)
(defun sum-numbers-in-region (start end)
  (interactive "r")
  (message "%s"
           (cl-reduce #'+
                      (split-string (buffer-substring start
                                                      end))
                      :key #'string-to-number)))

Because it uses string-to-number, it will treat the the token "1hello" as the number "1", but won't treat "hello2" as a number. It also works with decimals (e.g., 2.4).

Jordon Biondo
  • 12,332
  • 2
  • 41
  • 62
zck
  • 8,984
  • 2
  • 31
  • 65
5

Specifically for your case, assuming this is the entire text of the buffer:

  1. M-xreplace-regexpRET[^0-9]+RET+RET
  2. C-x h - select all.
  3. C-x * e (make sure there is no trailing plus sign).
wvxvw
  • 11,222
  • 2
  • 30
  • 55
  • The replace me with a trailing + at the end of the line which calc-mode didn't like: it worked when I removed that. Interesting answer though a bit inconvenient for this simple use case. – Croad Langshan Apr 26 '15 at 21:50
  • 4
    Another `calc` solution: mark region, `C-x * g` to grab, `V u` (`calc-unpack`), then tap `+` until you've summed all the numbers. – nanny Apr 27 '15 at 13:12
2

I don't know of a built-in command that does this, but you could make your own:

(defun sum-region-nums (beg end)
  (interactive "r")
  (save-excursion
    (goto-char beg)
    (let (nums total)
      (while (re-search-forward "\\b[0-9]+\\(\\.[0-9]+\\)*\\b" end t)
        (push (string-to-number (match-string-no-properties 0))
              nums))
      (setq total (apply #'+ nums))
      (message "%s" total)
      total)))
Jordon Biondo
  • 12,332
  • 2
  • 41
  • 62
Kyle Meyer
  • 6,914
  • 26
  • 22
  • @zck's answer is nicer (as long as you're ok with the number being part of the word). The regexp in this answer would need to be extended to support negative numbers. – Kyle Meyer Apr 26 '15 at 20:04
2

If you have a region that contains a formula:

234+44+12+4+4

You can mark the region and run C-x * g to evaluate in a new buffer.

(calc-dispatch-help ARG)
For moving data into and out of Calc:

  G  calc-grab-region.  Grab the region defined by mark and point into Calc.
Matt Kneiser
  • 121
  • 3
0

Here is a slight modification to zck's answer which also handles numbers embedded within other content. To accomplish this, it splits the text on any non-number characters.

 (defun ketbra/sum-numbers-in-region (start end)
    (interactive "r")
       (message "%s"
          (cl-reduce #'+
                    (split-string (buffer-substring start
                                                    end)
                                  "[^0-9\.\-]")
                    :key #'string-to-number)))
ketbra
  • 1
  • 1