package bakeoff import ( "encoding/json" "fmt" "sort" "strings" ) // MedianReadability combines several scorer replies: each score is the // median across replies, and the notes come from the reply whose overall // score is the median. Replies may wrap the JSON in a code fence. func MedianReadability(replies []string) (Readability, error) { type reply struct { Naming, Structure, Traceability, Errors, Comments, Overall float64 Notes string } var rs []reply for _, s := range replies { i, j := strings.Index(s, "{"), strings.LastIndex(s, "}") if i < 0 || j < i { return Readability{}, fmt.Errorf("no JSON object in reply %q", s) } var r reply if err := json.Unmarshal([]byte(s[i:j+1]), &r); err != nil { return Readability{}, err } rs = append(rs, r) } if len(rs) == 0 { return Readability{}, fmt.Errorf("no replies") } med := func(get func(reply) float64) float64 { v := make([]float64, len(rs)) for i, r := range rs { v[i] = get(r) } return median(v) } out := Readability{ Naming: med(func(r reply) float64 { return r.Naming }), Structure: med(func(r reply) float64 { return r.Structure }), Traceability: med(func(r reply) float64 { return r.Traceability }), Errors: med(func(r reply) float64 { return r.Errors }), Comments: med(func(r reply) float64 { return r.Comments }), Overall: med(func(r reply) float64 { return r.Overall }), } byOverall := append([]reply(nil), rs...) sort.SliceStable(byOverall, func(i, j int) bool { return byOverall[i].Overall < byOverall[j].Overall }) out.Notes = byOverall[len(byOverall)/2].Notes return out, nil }