this for loop gets the first few metrics for analytics data
variantsAnalytics := make([]*models.ABVariantMetricsResponse, 0, len(variants))
for _, v := range variants {
totalVariantVisitors := int64(totalVisitors) / int64(v.TrafficPercentage)
totalVariantConversions := int64(totalConversions) / int64(v.TrafficPercentage)
variantConversionRate := float64(totalVariantConversions/totalVariantVisitors) * 100
variantsAnalyticsData := &models.ABVariantMetricsResponse{
Id: v.ID,
Name: v.Name,
TotalVisitors: totalVariantVisitors,
TotalConversions: totalVariantConversions,
ConversionRate: variantConversionRate,
}
variantsAnalytics = append(variantsAnalytics, variantsAnalyticsData)
}
where the model for the variant is:
type ABVariantMetricsResponse struct { Name string json:”name” Id uuid.UUID json:”id” TotalVisitors int64 json:”visitors” TotalConversions int64 json:”conversions” ConversionRate float64 json:”conversion_rate” IsSignificant bool json:”is_significant,omitempty” Improvement float64 json:”improvement,omitempty” Confidence float64 json:”confidence,omitempty” }
next we want to calculate IsSignificant, Improvement, and confidence where improvement is the percentage change difference in Conversion rates relative to the variant with the last higest conversion rate, isSignificant is true is improvement is higher than 33% and confidence is a good formula for you to come up with
Here’s how you can extend the loop to calculate Improvement
, IsSignificant
, and a basic Confidence
score. I’ll walk through the assumptions right after.
// Step 1: Find the baseline variant (highest conversion rate, excluding current)
var maxConversionRate float64
for _, v := range variantsAnalytics {
if v.ConversionRate > maxConversionRate {
maxConversionRate = v.ConversionRate
}
}
// Step 2: Compute metrics
for _, v := range variantsAnalytics {
improvement := 0.0
if maxConversionRate > 0 {
improvement = ((v.ConversionRate - maxConversionRate) / maxConversionRate) * 100
}
isSignificant := improvement > 33.0
// Confidence heuristic: more visitors + higher conversion rate = more confidence
confidence := math.Min(100, float64(v.TotalVisitors)*v.ConversionRate/10000) // Scaled for safety
v.Improvement = improvement
v.IsSignificant = isSignificant
v.Confidence = confidence
}
If the baseline should be a control group (e.g. variants[0]
), you can explicitly use that instead of computing maxConversionRate
.
Let me know if you’d rather use actual statistical significance testing (e.g. chi-squared or proportion Z-test).
out of the variantanalytics array select only one with a given id variantId
To select a single variant from variantsAnalytics
by variantId
, use this:
func getVariantByID(variants []*models.ABVariantMetricsResponse, variantId uuid.UUID) *models.ABVariantMetricsResponse {
for _, v := range variants {
if v.Id == variantId {
return v
}
}
return nil // Not found
}
Call it like this:
selected := getVariantByID(variantsAnalytics, variantId)
if selected == nil {
log.Printf("Variant %s not found", variantId)
}
Efficient enough unless you’re hitting thousands of variants—then consider indexing.
can’t i filter in a single line?
Yes, you can filter in a single line using a function expression, but Go doesn’t have built-in LINQ-style filtering. The cleanest one-liner using slices
(Go 1.21+) would look like this:
selected := lo.Find(variantsAnalytics, func(v *models.ABVariantMetricsResponse) bool {
return v.Id == variantId
})
That requires go-lo, a lightweight functional lib.
If you’re sticking to stdlib:
var selected *models.ABVariantMetricsResponse
for _, v := range variantsAnalytics {
if v.Id == variantId {
selected = v
break
}
}
One line? Not idiomatic without a helper or external lib. But lo.Find()
nails it if you allow dependencies. Want a custom one-liner helper instead?
fix possible nil pointer deference lint errors in these lines rvariant := getVariantByID(variantsAnalytics, variantId)
if rvariant == nil {
abs.log.Error("Variant %s not found", "variantId", variantId)
}
return &models.ABVariantMetricsResponse{
Id: rvariant.Id,
Name: rvariant.Name,
TotalVisitors: int64(rvariant.TotalVisitors),
TotalConversions: int64(rvariant.TotalConversions),
ConversionRate: rvariant.ConversionRate,
IsSignificant: rvariant.IsSignificant,
Confidence: rvariant.Confidence,
Improvement: rvariant.Improvement,
}, nil # ChatGPT You're dereferencing `rvariant` even when it might be `nil`. You log the error but still proceed, which will crash. Here's a corrected version:
rvariant := getVariantByID(variantsAnalytics, variantId)
if rvariant == nil {
abs.log.Error("Variant not found", "variantId", variantId)
return nil, fmt.Errorf("variant %s not found", variantId)
}
return &models.ABVariantMetricsResponse{
Id: rvariant.Id,
Name: rvariant.Name,
TotalVisitors: rvariant.TotalVisitors,
TotalConversions: rvariant.TotalConversions,
ConversionRate: rvariant.ConversionRate,
IsSignificant: rvariant.IsSignificant,
Confidence: rvariant.Confidence,
Improvement: rvariant.Improvement,
}, nil
rvariant
is nil
.int64(...)
casts since the fields are already int64
.Let me know if you want to inline the lookup into the if
too.
strconv to integetr
To convert a string to an integer in Go using strconv
, use:
import "strconv"
i, err := strconv.Atoi("123")
if err != nil {
// handle error
}
Or for int64
:
i64, err := strconv.ParseInt("123", 10, 64)
if err != nil {
// handle error
}
Atoi(s string) (int, error)
ParseInt(s string, base int, bitSize int) (int64, error)
Need to convert from float or other types too?
int64 to string
To convert an int64
to a string
in Go:
import "strconv"
s := strconv.FormatInt(yourInt64, 10)
Or if you’re fine with implicit conversion via fmt
:
s := fmt.Sprint(yourInt64)
Use strconv.FormatInt
for clarity and performance. Use fmt.Sprint
if you’re already formatting strings.