package gofakeit import ( "errors" "math/rand" ) // Weighted will take in an array of options and weights and return a random selection based upon its indexed weight func Weighted(options []any, weights []float32) (any, error) { return weighted(globalFaker.Rand, options, weights) } // Weighted will take in an array of options and weights and return a random selection based upon its indexed weight func (f *Faker) Weighted(options []any, weights []float32) (any, error) { return weighted(f.Rand, options, weights) } // Weighted will take in an array of options and weights and return a random selection based upon its indexed weight func weighted(r *rand.Rand, options []any, weights []float32) (any, error) { ol := len(options) wl := len(weights) // If options length is 1 just return it back if ol == 1 { return options[0], nil } // Make sure they are passing in options if ol == 0 { return nil, errors.New("didnt pass options") } // Make sure they are passing in weights if wl == 0 { return nil, errors.New("didnt pass weights") } // Make sure they are passing in the same length if ol != wl { return nil, errors.New("options and weights need to be the same length") } // Compute the discrete cumulative density from the sum of the weights cdf := make([]float32, wl) var sumOfWeights float32 = 0.0 for i, weight := range weights { if i > 0 { cdf[i] = cdf[i-1] + weight sumOfWeights += weight continue } cdf[i] = weight sumOfWeights += weight } // Get rand value from a multple of sumOfWeights randSumOfWeights := r.Float32() * sumOfWeights var l int = 0 var h int = wl - 1 for l <= h { m := l + (h-l)/2 if randSumOfWeights <= cdf[m] { if m == 0 || (m > 0 && randSumOfWeights > cdf[m-1]) { return options[m], nil } h = m - 1 } else { l = m + 1 } } return nil, errors.New("end of function") } func addWeightedLookup() { AddFuncLookup("weighted", Info{ Display: "Weighted", Category: "misc", Description: "Randomly select a given option based upon an equal amount of weights", Example: "[hello, 2, 6.9],[1, 2, 3] => 6.9", Output: "any", Params: []Param{ {Field: "options", Display: "Options", Type: "[]string", Description: "Array of any values"}, {Field: "weights", Display: "Weights", Type: "[]float", Description: "Array of weights"}, }, Generate: func(r *rand.Rand, m *MapParams, info *Info) (any, error) { options, err := info.GetStringArray(m, "options") if err != nil { return nil, err } weights, err := info.GetFloat32Array(m, "weights") if err != nil { return nil, err } optionsInterface := make([]any, len(options)) for i, o := range options { optionsInterface[i] = o } return weighted(r, optionsInterface, weights) }, }) }