The data is naturally a map from student-number (a string) to a vector of grades:
(def grades-1 {"s18129" [100 70 85 71 85]
"s18121" [80 75 85 81 85]
"r18131" [75 60 80 56 75]})
... and a vector of weights to be applied to the above:
(def grade-weights [10 20 15 25 30])
... turning out a map from student-number to final grade. How can we factor this?
A function to calculate the average according to a given weights
vector is
(fn [grades] (/ (reduce + 0.0 (map * weights grades)) (reduce + weights)))
... where
- the initial value of
0.0
forces the use of floating point arithmetic and - the weights need not add up to 1.0 or 100 or any number in particular. Scaling is included.
The following converts each value of a-map
into a function f
of that value:
(fn [f a-map] (into {} (map (fn [[k v]] [k (f v)]) a-map)))
So a function to deliver a map of final grades from a map of grade vectors and a weighting vector is ...
(defn av-grades [gt weights]
(let [weight-av (fn [grades]
(/ (reduce + 0.0 (map * weights grades)) (reduce + weights)))
convert-map (fn [f a-map]
(into {} (map (fn [[k v]] [k (f v)]) a-map)))]
(convert-map weight-av gt)))
And, sure enough, ...
=>(av-grades grades-1 grade-weights)
{"r18131" 68.0, "s18121" 81.5, "s18129" 80.0}