Files
elasticsearch-js/scripts/generate/logs/samples/weighted_list.js
2014-02-27 15:36:52 -07:00

124 lines
2.2 KiB
JavaScript

/**
* @class WeightedList
* @constructor
*/
module.exports = WeightedList;
var _ = require('../../../../src/lib/utils');
function WeightedList(list) {
Array.call(this);
_.forEach(list, _.bindKey(this, 'push'));
}
_.inherits(WeightedList, Array);
/**
* Add a value to the weighted list.
* @method add
* @param {Any} value
* @param {Number} [weight] Optional. Defaults to 1.
* @return {Number} The index of the item that was added.
*/
WeightedList.prototype.push = function (weight, value) {
if (!weight && weight !== 0) {
weight = 1;
}
Array.prototype.push.call(this, {
value: value,
weight: weight
});
delete this._sum;
delete this._totals;
return this.length - 1;
};
WeightedList.prototype.get = function () {
return this[this._randomIndex()].value;
};
/**
* Returns an index by weighted random distribution.
* @method _randomIndex
* @protected
* @return {Number}
*/
WeightedList.prototype._randomIndex = function () {
var maximumIndex,
me = this,
middleIndex,
minimumIndex = 0,
random,
sum = me._sum,
total,
totals = me._totals;
if (!sum || !totals) {
me._update();
sum = me._sum;
totals = me._totals;
if (!sum || !totals || !totals.length) {
return null;
}
}
maximumIndex = totals.length - 1;
random = Math.random() * sum;
while (maximumIndex >= minimumIndex) {
middleIndex = (maximumIndex + minimumIndex) / 2;
if (middleIndex < 0) {
middleIndex = 0;
} else {
middleIndex = Math.floor(middleIndex);
}
total = totals[middleIndex];
if (random === total) {
middleIndex += 1;
break;
} else if (random < total) {
if (middleIndex && random > totals[middleIndex - 1]) {
break;
}
maximumIndex = middleIndex - 1;
} else if (random > total) {
minimumIndex = middleIndex + 1;
}
}
return middleIndex;
};
/**
* Updates chached data for achieving weighted random distribution.
* @method _update
* @chainable
* @protected
*/
WeightedList.prototype._update = function () {
var me = this,
sum = 0,
totals = [];
_.each(me, function (item) {
sum += item.weight;
totals.push(sum);
});
me._sum = sum;
me._totals = totals;
return me;
};