elegant way to count items

后端 未结 9 1962
感情败类
感情败类 2020-12-10 15:29

I have a list shaped like this:

  \'((\"Alpha\" .  1538)
    (\"Beta\"  .  8036)
    (\"Gamma\" .  8990)
    (\"Beta\"  .  10052)
    (\"Alpha\" .  12837)
           


        
相关标签:
9条回答
  • 2020-12-10 15:37
    (defun freqs (list &optional test key)
      (let ((h (make-hash-table :test test)))
        (dolist (x list)
          (let ((key (if key (funcall key x) x)))
            (puthash key (1+ (gethash key h 0)) h)))
        (let ((r nil))
          (maphash #'(lambda (k v) (push (cons k v) r)) h)
          (sort r #'(lambda (x y) (< (cdr x) (cdr y)))))))
    
    (freqs '(("Alpha" .  1538)
             ("Beta"  .  8036)
             ("Gamma" .  8990)
             ("Beta"  .  10052)
             ("Alpha" .  12837)
             ("Beta"  .  13634)
             ("Beta"  .  14977)
             ("Beta"  .  15719)
             ("Alpha" .  17075)
             ("Rho"   .  18949)
             ("Gamma" .  21118)
             ("Gamma" .  26923)
             ("Alpha" .  31609))
           #'equal #'car)
    
    0 讨论(0)
  • 2020-12-10 15:39

    Using high-order functions sort and reduce.

    First sorting (using string<) then reducing (counting consecutive string= values in cons cells):

    (reduce (lambda (r e)
              (if (and r (string= (caar r) e))
                  (cons
                   (cons (caar r) (1+ (cdar r)))
                   (cdr r))
                (cons (cons e  1) r)))
            (sort (mapcar 'car alist) 'string<)
            :initial-value nil)
    
    0 讨论(0)
  • 2020-12-10 15:42

    This is pretty easy and very straightforward using the dash library:

    (require 'dash)
    
    (defun frequencies (values)
      "Return an alist indicating the frequency of values in VALUES list."
      (mapcar (-lambda ((value . items))
              (cons value (length items)))
            (-group-by #'identity
                       values)))
    
    (frequencies (mapcar #'car my-list))
    
    0 讨论(0)
  • 2020-12-10 15:47

    Here's what I think is an elegant functional solution using Emacs' alist functions, yielding a reusable frequencies function similar to Eli's answer:

    (defun frequencies (vals)
      (reduce
       (lambda (freqs key)
         (cons (cons key (+ 1 (or (cdr (assoc key freqs)) 0)))
               (assq-delete-all-with-test key freqs 'equal)))
       vals
       :initial-value nil)))
    
    (frequencies (mapcar 'car
                         '(("Alpha" .  1538)
                           ("Beta"  .  8036)
                           ("Gamma" .  8990)
                           ("Beta"  .  10052)
                           ("Alpha" .  12837)
                           ("Beta"  .  13634)
                           ("Beta"  .  14977)
                           ("Beta"  .  15719)
                           ("Alpha" .  17075)
                           ("Rho"   .  18949)
                           ("Gamma" .  21118)
                           ("Gamma" .  26923)
                           ("Alpha" .  31609))))
    => (("Alpha" . 4) ("Gamma" . 3) ("Rho" . 1) ("Beta" . 5))
    
    0 讨论(0)
  • 2020-12-10 15:49

    Using Common Lisp extensions:

    (require 'cl)
    (loop with result = nil
          for (key . dummy) in original-list
          do (incf (cdr (or (assoc key result)
                            (first (push (cons key 0) result)))))
          finally return (sort result
                               (lambda (a b) (string< (car a) (car b)))))
    

    You can just say finally return result if you don't care about sorting the final result.

    0 讨论(0)
  • 2020-12-10 15:49
    (require 'cl)
    (defun count-uniq (list)
      (let ((k 1) (list (sort (mapcar #'car list) #'string<)))
        (loop for (i . j) on list
              when (string= i (car j)) do (incf k)
              else collect (cons i k) and do (setf k 1))))
    
    0 讨论(0)
提交回复
热议问题