Skip to content

Instantly share code, notes, and snippets.

@shaunparker
Last active August 14, 2019 20:16
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save shaunparker/97397b2725b6ffb56d106372ac74d838 to your computer and use it in GitHub Desktop.
Save shaunparker/97397b2725b6ffb56d106372ac74d838 to your computer and use it in GitHub Desktop.
A coding exercise for merging media segments and calculating the total duration of a piece of media that was watched.
(ns media-segments)
(defn merge-segments
"Merges two overlapping segments into one segment."
[[start1 end1] [start2 end2]]
[(min start1 start2) (max end1 end2)])
(defn normalize-segments
"Takes a sequence of segments, sorts them, and merges any that overlap.
[[1 5] [10 15]] ─ ─ ─ ─ ─ ─▶ [[1 5] [10 15]]
[[1 3] [6 12] [5 8]] ─ ─ ─ ─▶ [[1 3] [5 12]]
──┬─ ─┬─ ──┬─
└──┬──┘ │
┌───┴──────────────────────────┴──┐
│ Overlapping segments are merged │
└─────────────────────────────────┘"
[segments]
(->> (sort segments)
(reduce (fn [segments segment]
(let [previous-segment (last segments)
[_prev-start prev-end] previous-segment
[start _end] segment]
(if (and previous-segment
(<= start prev-end))
;; ┌──────────────────────────────┐
;; │ [[1 3] [5 8]] [6 12] │
;; │ prev-end◀──┘ └──▶start │
;; │*merge when start <= prev-end*│
;; └──────────────────────────────┘
(conj (pop segments)
(merge-segments previous-segment segment))
;; first segment or no overlap, just add it
(conj segments segment))))
[])))
(defn segment->duration
"Calculates the units between the start and end of the segment, where
the start is inclusive and the end is exclusive, so [1 1] ─▶ 0, not 1."
[[start end]]
(- end start))
(defn segments->duration
"Takes a sequence of segments [[1 2] [2 4] [8 12]], normalizes them, and
returns the total duration contained in all the normalized segments.
[[1 1]] ─▶ 0
[[1 2]] ─▶ 1
[[1 2] [2 3]] ─▶ 2
[[1 2] [4 8]] ─▶ 5"
[segments]
(->> (normalize-segments segments)
(map segment->duration)
(reduce +)))
(defn watched-ratio
"Takes a sequence of segments that were watched and the total-duration of
the watched media and returns the ratio of media the user watched."
[segments total-duration]
(if (zero? total-duration)
0
(/ (segments->duration segments) total-duration)))
(comment
(normalize-segments [[1 1]])
(normalize-segments [[1 10] [2 12] [14 21]])
(normalize-segments [[1 10] [2 12] [14 21] [16 28] [24 38] [61 65]])
(segments->duration [[1 2] [2 3]])
(segments->duration [[1 2] [4 8]])
(watched-ratio [[0 10] [2 12] [14 22]] 60)
(= (watched-ratio [[0 0]] 0) 0)
(= (watched-ratio [[0 10] [2 12] [14 22]] 60)
(/ 20 60)))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment