Allow value-returning function in weightedArrayElement
Clear and concise description of the problem
I want to generate a weighted distribution where each member of the distribution is itself a randomly generated item with specific configuration.
For example, I want to generate a set of fake movies where the release date is random dates weighted into decades (generate more recent movies than older movies).
Suggested solution
Allow faker.helpers.weightedArrayElement to take a () => T in addition to just T for value:
faker.helpers.weightedArrayElement([
{weight: 1, value: () => faker.date.between({from: "1980-01-01", to: "1989-12-31"})},
{weight: 2, value: () => faker.date.between({from: "1990-01-01", to: "1999-12-31"})},
{weight: 3, value: () => faker.date.between({from: "2000-01-01", to: "2009-12-31"})},
{weight: 4, value: () => faker.date.between({from: "2010-01-01", to: "2010-12-31"})},
])
Alternative
Add a new separate method for this kind of weighted randomized distribution, but the code change to support this would be minimal.
Additional context
No response
Thank you for your feature proposal.
We marked it as "waiting for user interest" for now to gather some feedback from our community:
- If you would like to see this feature be implemented, please react to the description with an up-vote (:+1:).
- If you have a suggestion or want to point out some special cases that need to be considered, please leave a comment, so we are aware about them.
We would also like to hear about other community members' use cases for the feature to give us a better understanding of their potential implicit or explicit requirements.
We will start the implementation based on:
- the number of votes (:+1:) and comments
- the relevance for the ecosystem
- availability of alternatives and workarounds
- and the complexity of the requested feature
We do this because:
- There are plenty of languages/countries out there and we would like to ensure that every method can cover all or almost all of them.
- Every feature we add to faker has "costs" associated to it:
- initial costs: design, implementation, reviews, documentation
- running costs: awareness of the feature itself, more complex module structure, increased bundle size, more work during refactors
Workaround
Call the method after it has been selected by weightedArrayElement.
faker.helper.weightedArrayElement(...)()
Evaluating the functions inside of weightedArrayElements will make it impossible for the method to return functions itself, in case the functions itself are the expected values.
Alternative (for this usecase)
Use #3375 (expoenentialDistribution) and deduct the ms from the date.
- #3375
new Date(now.getTime() - faker.number.int({max: 1000*60*60*24*365*50, distribution: exponentialDistribution(?)}));
Evaluating the functions inside of weightedArrayElements will make it impossible for the method to return functions itself, in case the functions itself are the expected values.
Fair, it might need to be valueFunction: () => T with value and valueFunction being a oneof
Workaround
Call the method after it has been selected by weightedArrayElement.
faker.helper.weightedArrayElement(...)()
This is feasible in most scenarios, but is a little awkward (not totally unworkable) in the broader scenario I'm considering.
This is feasible in most scenarios, but is a little awkward (not totally unworkable) in the broader scenario I'm considering.
Could you explain this to me? As far as I'm concerned, functions that return another function is a pretty common pattern in JS:
- most of Angular's validators (example maxValidator())
- all of rxjs' pipe operators (example map())
- NodeJS'
util.promisify
Just to name a few.
If the "awkward" part is the double parentheses, you can save the returned function to a variable first. But I guess this comes down to preferences:
const dateBetweenFn = faker.helpers.weightedArrayElement([
{weight: 1, value: () => faker.date.between({from: "1980-01-01", to: "1989-12-31"})},
{weight: 2, value: () => faker.date.between({from: "1990-01-01", to: "1999-12-31"})},
{weight: 3, value: () => faker.date.between({from: "2000-01-01", to: "2009-12-31"})},
{weight: 4, value: () => faker.date.between({from: "2010-01-01", to: "2010-12-31"})},
]);
const date = dateBetweenFn();
Could you explain this to me? As far as I'm concerned, functions that return another function is a pretty common pattern in JS:
Honestly it's probably fine to close this bug though I do think the feature would be useful - I'm playing around with a library that allowed for structured synthetic data generation. I built a "LazyFaker" proxy that matched the Faker API but returned a no-argument closure function instead because that allowed defining things with a more static definition that didn't itself have to be wrapped in a function.
The library worked by traversing the tree of the object and executing any functions it found. If this feature existed, then that could all happen in a single pass. Without this feature, it needs to do the traversal multiple times until no function leaf nodes remain.
Mind sharing some code or links?