0

In the following example, please assume that we are using time-to-seconds to convert each times-stamp into a decimal representation. I have already converted the time-stamps to seconds in this example. Each node has at least one time-stamp, but there might be more than one time-stamp per node. The current node with corresponding time-stamp is represented by a cons cell with the cdr being t. The time-stamps will always be unique. The nodes are vectors with multiple elements.

(defun goto-node (time-stamp n)
"Go to the desired node commencing from an existing TIME-STAMP.
If N is positive, then go forwards in time by that number of N time-stamps.
If N is negative, then backwards in time by that number of N time-stamps.
If node does not exist, return nil; otherwise, return node and corresponding time-stamp."

INSERT MAGIC HERE)

BEGIN WITH:

'(([node1] ((5.6) (3.7) (11.7) (8.2)))
  ([node2] ((4.4) (9.9) (6.1 . t)))
  ([node3] ((7.5) (2.3) (1.5)))
  ([node4] ((10.3))))

EXAMAPLES:

(goto-node 6.1 -1) => '([node1] (5.6))

(goto-node 6.1 -4) => '([node3] (2.3))

(goto-node 6.1 1) => '([node3] (7.5))

(goto-node 6.1 4) => '([node4] (10.3))

lawlist
  • 18,826
  • 5
  • 37
  • 118

1 Answers1

3

Generate a complete (rassoc-) map assigning time-stamps to nodes at first. Afterwards sort and then locate time-stamp in the sorted list. From there you can go forward and backward.

(defvar goto-node-timestamp-tolerance 1e-5
  "Tolerance for testing equality of timestamps.")

(defun goto-node (list time-stamp n)
  "Go to the desired node commencing from an existing TIME-STAMP in LIST.
If N is positive, then go forwards in time by that number of N time-stamps.
If N is negative, then backwards in time by that number of N time-stamps.
If node does not exist, return nil; otherwise, return node and corresponding time-stamp."
  (let* ((full-list (apply #'append
                           (loop for node-stamps in list
                                 collect (mapcar (lambda (stamp) (list (car node-stamps) stamp)) (cadr node-stamps)))))
         (full-list (cl-sort full-list (lambda (node-stamp1 node-stamp2) (< (caadr node-stamp1) (caadr node-stamp2)))))
         (current-pos (cl-position-if (lambda (node-stamp) (< (abs (- (caadr node-stamp) time-stamp)) goto-node-timestamp-tolerance)) full-list)))
    (assert current-pos nil "Timestamp not found in list!")
    (nth (max (min (+ current-pos n) (1- (length full-list))) 0) full-list)))

(setq l '(([node1] ((5.6) (3.7) (11.7) (8.2)))
  ([node2] ((4.4) (9.9) (6.1 . t)))
  ([node3] ((7.5) (2.3) (1.5)))
  ([node4] ((10.3)))))

(goto-node l 6.1 -1) ; => '([node3] (5.6))

(goto-node l 6.1 -4) ; => '([node3] (2.3))

(goto-node l 6.1 1) ; => '([node3] (7.5))

(goto-node l 6.1 4) ; => '([node4] (10.3))
Tobias
  • 32,569
  • 1
  • 34
  • 75
  • A few observations: **(1)** `time-to-seconds` creates a decimal of six places, so we should probably be using `1e6`. **(2)** The conversion from `time-to-seconds` and `seconds-to-time` again is not precise and the original timestamp is not the exact same after going through the washing machine and dryer. Therefore, I created a list with the best of both worlds -- e.g, `(([node1] (((1493686266.992367 (22791 55290 992367 0)) t))))`. By adding a `car` to the front of each `caadr`, the seconds are used to do comparisons while the result still contains the original timestamp unchanged. – lawlist May 02 '17 at 01:08