react-mapfilter
Version:
These components are designed for viewing data in Mapeo. They share a common interface:
389 lines (322 loc) • 12.4 kB
JavaScript
;
var _interopRequireWildcard = require("@babel/runtime-corejs3/helpers/interopRequireWildcard");
var _interopRequireDefault = require("@babel/runtime-corejs3/helpers/interopRequireDefault");
var _Object$defineProperty = require("@babel/runtime-corejs3/core-js-stable/object/define-property");
require("core-js/modules/es.number.constructor");
require("core-js/modules/es.regexp.exec");
require("core-js/modules/es.string.split");
_Object$defineProperty(exports, "__esModule", {
value: true
});
exports.default = createMemoizedStats;
exports.diffArrays = diffArrays;
exports.statReduce = statReduce;
var _concat = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/instance/concat"));
var _map = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/instance/map"));
var _indexOf = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/instance/index-of"));
var _filter = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/instance/filter"));
var _values = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/instance/values"));
var _stringify = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/json/stringify"));
var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime-corejs3/helpers/slicedToArray"));
var _forEach = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/instance/for-each"));
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime-corejs3/helpers/defineProperty"));
var _map2 = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/map"));
var _cloneDeep = _interopRequireDefault(require("clone-deep"));
var _flat_object_entries = require("../../utils/flat_object_entries");
var _value_types = require("./value_types");
var valueTypes = _interopRequireWildcard(require("../../constants/value_types"));
var _helpers = require("../../utils/helpers");
// @flow
function defaultStats()
/*: FieldStatistic*/
{
var _ref;
return _ref = {}, (0, _defineProperty2.default)(_ref, valueTypes.STRING, {
count: 0,
values: new _map2.default()
}), (0, _defineProperty2.default)(_ref, valueTypes.NUMBER, {
count: 0,
values: new _map2.default()
}), (0, _defineProperty2.default)(_ref, valueTypes.DATE, {
count: 0,
values: new _map2.default()
}), (0, _defineProperty2.default)(_ref, valueTypes.DATETIME, {
count: 0,
values: new _map2.default()
}), (0, _defineProperty2.default)(_ref, valueTypes.BOOLEAN, {
count: 0,
values: new _map2.default([[true, 0], [false, 0]])
}), (0, _defineProperty2.default)(_ref, valueTypes.URL, 0), (0, _defineProperty2.default)(_ref, valueTypes.IMAGE_URL, 0), (0, _defineProperty2.default)(_ref, valueTypes.AUDIO_URL, 0), (0, _defineProperty2.default)(_ref, valueTypes.VIDEO_URL, 0), (0, _defineProperty2.default)(_ref, valueTypes.NULL, 0), (0, _defineProperty2.default)(_ref, valueTypes.UNDEFINED, 0), (0, _defineProperty2.default)(_ref, valueTypes.LOCATION, 0), _ref;
}
function createMemoizedStats() {
var stats
/*: Statistics*/
= {};
var dataMemo = [];
return function getStats(data
/*: Array<JSONObject>*/
)
/*: Statistics*/
{
if (data === dataMemo) return stats;
var _diffArrays = diffArrays(dataMemo, data),
added = _diffArrays.added,
removed = _diffArrays.removed;
if (!added.length && !removed.length) return (0, _cloneDeep.default)(stats);
if (removed.length) {
// Anything removed, calculate all the stats
// TODO: more efficient stats on removal
stats = {};
(0, _forEach.default)(data).call(data, function (item) {
return addItemStats(item, stats);
});
} else {
// Only added items -> only need to process new items
(0, _forEach.default)(added).call(added, function (item) {
return addItemStats(item, stats);
});
}
dataMemo = data;
return (0, _cloneDeep.default)(stats);
};
}
function addItemStats() {
var _context;
var item
/*: JSONObject*/
= arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
var stats
/*: Statistics*/
= arguments.length > 1 ? arguments[1] : undefined;
(0, _forEach.default)(_context = (0, _flat_object_entries.flatObjectEntries)(item)).call(_context, function (_ref2) {
var _ref3 = (0, _slicedToArray2.default)(_ref2, 2),
path = _ref3[0],
value = _ref3[1];
var key = (0, _stringify.default)(path);
if (!stats[key]) stats[key] = defaultStats();
addFieldStats(value, stats[key]);
});
}
function addFieldStats(value
/*: any*/
, fieldStats
/*: FieldStatistic*/
) {
var type = (0, _value_types.guessValueType)(value);
if (typeof fieldStats[type] === 'number') {
fieldStats[type] += 1;
return;
}
switch (type) {
case valueTypes.ARRAY:
var arrayStats = fieldStats[valueTypes.ARRAY];
if (arrayStats === undefined) {
arrayStats = fieldStats[valueTypes.ARRAY] = {
count: 0,
lengthMin: +Infinity,
lengthMax: -Infinity,
valueStats: defaultStats()
};
}
addArrayStats(value, arrayStats);
return;
case valueTypes.STRING:
addStringStats(value, fieldStats[valueTypes.STRING]);
return;
case valueTypes.NUMBER:
addNumberStats(value, fieldStats[valueTypes.NUMBER]);
return;
case valueTypes.DATE:
addDateStats(value, fieldStats[valueTypes.DATE]);
return;
case valueTypes.DATETIME:
addDateTimeStats(value, fieldStats[valueTypes.DATETIME]);
return;
case valueTypes.BOOLEAN:
fieldStats[valueTypes.BOOLEAN].count += 1;
(0, _values.default)(fieldStats[valueTypes.BOOLEAN]).set(value, (0, _values.default)(fieldStats[valueTypes.BOOLEAN]).get(value) + 1);
return;
default:
// $FlowFixMe - flow doesn't realise that type is not values above
fieldStats[type].count += 1;
}
}
function addArrayStats(value
/*: []*/
, stats
/*: $NonMaybeType<$ElementType<FieldStatistic, 'array'>>*/
) {
if (value.length > stats.lengthMax) stats.lengthMax = value.length;
if (value.length < stats.lengthMin) stats.lengthMin = value.length;
stats.count += 1;
(0, _forEach.default)(value).call(value, function (item) {
addFieldStats(item, stats.valueStats);
});
}
function addNumberStats(value
/*: number*/
, stats
/*: NumberStatistic*/
) {
stats.count += 1;
var _statReduce = statReduce(stats, value, stats.count - 1),
min = _statReduce.min,
max = _statReduce.max,
variance = _statReduce.variance,
mean = _statReduce.mean;
stats.min = min;
stats.max = max;
stats.variance = variance;
stats.mean = mean;
if ((0, _values.default)(stats).has(value)) (0, _values.default)(stats).set(value, (0, _values.default)(stats).get(value) + 1);else (0, _values.default)(stats).set(value, 1);
}
function addDateTimeStats(value
/*: string*/
, stats
/*: DateStatistic*/
) {
var dateAsNumber = +Date.parse(value);
stats.count += 1;
var _statReduce2 = statReduce({
mean: stats.mean !== undefined ? +Date.parse(stats.mean) : undefined
}, dateAsNumber, stats.count - 1),
mean = _statReduce2.mean;
stats.min = stats.min === undefined ? value : value < stats.min ? value : stats.min;
stats.max = stats.max === undefined ? value : value > stats.max ? value : stats.max;
stats.mean = mean !== undefined ? new Date(mean).toISOString() : undefined;
if ((0, _values.default)(stats).has(value)) (0, _values.default)(stats).set(value, (0, _values.default)(stats).get(value) + 1);else (0, _values.default)(stats).set(value, 1);
}
/** This requires slightly special treatment because date does not include time */
function addDateStats(value
/*: string*/
, stats
/*: DateStatistic*/
) {
var dateAsNumber = dateToNumber(value);
stats.count += 1;
var _statReduce3 = statReduce({
mean: stats.mean !== undefined ? dateToNumber(stats.mean) : undefined
}, dateAsNumber, stats.count - 1),
mean = _statReduce3.mean;
stats.min = stats.min === undefined ? value : value < stats.min ? value : stats.min;
stats.max = stats.max === undefined ? value : value > stats.max ? value : stats.max;
stats.mean = mean !== undefined ? numberToDate(mean) : undefined;
if ((0, _values.default)(stats).has(value)) (0, _values.default)(stats).set(value, (0, _values.default)(stats).get(value) + 1);else (0, _values.default)(stats).set(value, 1);
}
function addStringStats(value
/*: string*/
, stats
/*: StringStatistic*/
) {
stats.count += 1;
var lengthStats = statReduce({
min: stats.lengthMin,
max: stats.lengthMax,
variance: stats.lengthVariance,
mean: stats.lengthMean
}, value.length, stats.count - 1);
var wordStats = statReduce({
min: stats.wordsMin,
max: stats.wordsMax,
variance: stats.wordsVariance,
mean: stats.wordsMean
}, value.split(' ').length, stats.count - 1);
stats.lengthMin = lengthStats.min;
stats.lengthMax = lengthStats.max;
stats.lengthVariance = lengthStats.variance;
stats.lengthMean = lengthStats.mean;
stats.wordsMin = wordStats.min;
stats.wordsMax = wordStats.max;
stats.wordsVariance = wordStats.variance;
stats.wordsMean = wordStats.mean;
if ((0, _values.default)(stats).has(value)) (0, _values.default)(stats).set(value, (0, _values.default)(stats).get(value) + 1);else (0, _values.default)(stats).set(value, 1);
}
/**
* Compare two arrays of objects and return items in the second but not in the
* first (added) and items in the first but not in the second (removed).
* Compares using strict equality.
*/
function diffArrays(oldArray
/*: Array<Object>*/
, newArray
/*: Array<Object>*/
)
/*: { added: Array<Object>, removed: Array<Object> }*/
{
var added = (0, _filter.default)(newArray).call(newArray, function (v) {
return (0, _indexOf.default)(oldArray).call(oldArray, v) === -1;
});
var removed = (0, _filter.default)(oldArray).call(oldArray, function (v) {
return (0, _indexOf.default)(newArray).call(newArray, v) === -1;
});
return {
added: added,
removed: removed
};
}
/*:: type MathStat = {
mean?: number,
variance?: number,
min?: number,
max?: number
}*/
/**
* Reducer that computes running mean, variance, min and max
* Adapted from http://www.johndcook.com/blog/standard_deviation/
* @param {Object} p The previous value for the analysis
* @param {Number} x New value to be included in analysis
* @param {Number} i zero-based index of the current element being processed
* @return {Object} New analysis including `x`
*/
function statReduce(_ref4, x
/*: number*/
, i
/*: number*/
)
/*: $ObjMap<MathStat, <V>(V) => $NonMaybeType<V>>*/
{
var _ref4$mean = _ref4.mean,
mean = _ref4$mean === void 0 ? NaN : _ref4$mean,
_ref4$variance = _ref4.variance,
variance = _ref4$variance === void 0 ? NaN : _ref4$variance,
_ref4$min = _ref4.min,
min = _ref4$min === void 0 ? +Infinity : _ref4$min,
_ref4$max = _ref4.max,
max = _ref4$max === void 0 ? -Infinity : _ref4$max;
mean = isNaN(mean) ? 0 : mean;
x = x instanceof Date ? +x : x;
var newMean = mean + (x - mean) / (i + 1);
return {
mean: newMean,
min: x < min ? x : min,
max: x > max ? x : max,
variance: i < 1 ? 0 : (variance * i + (x - mean) * (x - newMean)) / (i + 1)
};
}
/** Convert date in the format YYYY-MM-DD to a number */
function dateToNumber(value
/*: string*/
)
/*: number*/
{
var _context2;
var _value$split$map = (0, _map.default)(_context2 = value.split('-')).call(_context2, Number),
_value$split$map2 = (0, _slicedToArray2.default)(_value$split$map, 3),
year = _value$split$map2[0],
month = _value$split$map2[1],
day = _value$split$map2[2]; // Add 12 hours -> middle of day
return new Date(year, month - 1, day).getTime() + 12 * 60 * 60 * 1000;
}
function numberToDate(value
/*: number*/
)
/*: string*/
{
var _context3, _context4;
var date = new Date(value);
var YYYY = date.getFullYear();
var MM = (0, _helpers.leftPad)(date.getMonth() + 1 + '', 2, '0');
var DD = (0, _helpers.leftPad)(date.getDate() + '', 2, '0');
return (0, _concat.default)(_context3 = (0, _concat.default)(_context4 = "".concat(YYYY, "-")).call(_context4, MM, "-")).call(_context3, DD);
}
//# sourceMappingURL=statistics.js.map