Last active
August 14, 2019 20:16
-
-
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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(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