UNPKG

@italia-tools/faker

Version:

Italian-specific fake data generator based on Faker.js

85 lines (82 loc) 2.96 kB
'use strict'; class WeightedRandomSelector { /** * Creates a WeightedRandomSelector that respects actual weights * @param items Array of items with weights * @param isSorted boolean indicating if items are pre-sorted */ constructor(items, isSorted = false) { this.SMALL_DATASET_THRESHOLD = 100; this.totalWeight = 0; if (items.length === 0) { throw new Error("Items array cannot be empty"); } this.isSmallDataset = items.length <= this.SMALL_DATASET_THRESHOLD; if (this.isSmallDataset) { this.initializeSmallDataset(items); } else { this.initializeLargeDataset(items, isSorted); } } initializeSmallDataset(items) { this.fastAccessArray = []; items.forEach(item => { // For small datasets, we keep direct representation this.fastAccessArray.push(...Array(item.weight).fill(item.value)); }); } /** * Initialization for large datasets using cumulative weights */ initializeLargeDataset(items, isSorted) { this.items = []; let accumulatedWeight = 0; // If not sorted, sort by descending weight to optimize search const workingItems = isSorted ? items : [...items].sort((a, b) => b.weight - a.weight); workingItems.forEach(item => { accumulatedWeight += item.weight; this.items.push({ value: item.value, weight: item.weight, accumulatedWeight }); }); this.totalWeight = accumulatedWeight; } /** * Returns a random item respecting actual weights */ select() { if (this.isSmallDataset && this.fastAccessArray) { return this.fastAccessArray[Math.floor(Math.random() * this.fastAccessArray.length)]; } const randomWeight = Math.random() * this.totalWeight; // Binary search to find element with correct cumulative weight return this.binarySearch(randomWeight); } binarySearch(targetWeight) { let left = 0; let right = this.items.length - 1; while (left <= right) { const mid = Math.floor((left + right) / 2); const item = this.items[mid]; if (mid === 0 || (this.items[mid - 1].accumulatedWeight <= targetWeight && item.accumulatedWeight > targetWeight)) { return item.value; } if (item.accumulatedWeight <= targetWeight) { left = mid + 1; } else { right = mid - 1; } } // Fallback to first element (this should never happen) return this.items[0].value; } selectMultiple(count) { return Array.from({ length: count }, () => this.select()); } } exports.WeightedRandomSelector = WeightedRandomSelector;