UNPKG

devextreme

Version:

HTML5 JavaScript Component Suite for Responsive Web Development

567 lines (564 loc) • 17.4 kB
/** * DevExtreme (esm/data/array_query.js) * Version: 21.1.4 * Build date: Mon Jun 21 2021 * * Copyright (c) 2012 - 2021 Developer Express Inc. ALL RIGHTS RESERVED * Read about DevExtreme licensing here: https://js.devexpress.com/Licensing/ */ import Class from "../core/class"; import { isFunction, isDefined } from "../core/utils/type"; import { each, map } from "../core/utils/iterator"; import { compileGetter, toComparable } from "../core/utils/data"; import { Deferred } from "../core/utils/deferred"; import { errors, handleError as handleDataError } from "./errors"; import dataUtils from "./utils"; var Iterator = Class.inherit({ toArray: function() { var result = []; this.reset(); while (this.next()) { result.push(this.current()) } return result }, countable: function() { return false } }); var ArrayIterator = Iterator.inherit({ ctor: function(array) { this.array = array; this.index = -1 }, next: function() { if (this.index + 1 < this.array.length) { this.index++; return true } return false }, current: function() { return this.array[this.index] }, reset: function() { this.index = -1 }, toArray: function() { return this.array.slice(0) }, countable: function() { return true }, count: function() { return this.array.length } }); var WrappedIterator = Iterator.inherit({ ctor: function(iter) { this.iter = iter }, next: function() { return this.iter.next() }, current: function() { return this.iter.current() }, reset: function() { return this.iter.reset() } }); var MapIterator = WrappedIterator.inherit({ ctor: function(iter, mapper) { this.callBase(iter); this.index = -1; this.mapper = mapper }, current: function() { return this.mapper(this.callBase(), this.index) }, next: function() { var hasNext = this.callBase(); if (hasNext) { this.index++ } return hasNext } }); var defaultCompare = function(xValue, yValue) { xValue = toComparable(xValue); yValue = toComparable(yValue); if (null === xValue && null !== yValue) { return -1 } if (null !== xValue && null === yValue) { return 1 } if (void 0 === xValue && void 0 !== yValue) { return 1 } if (void 0 !== xValue && void 0 === yValue) { return -1 } if (xValue < yValue) { return -1 } if (xValue > yValue) { return 1 } return 0 }; var SortIterator = Iterator.inherit({ ctor: function(iter, getter, desc, compare) { if (!(iter instanceof MapIterator)) { iter = new MapIterator(iter, this._wrap) } this.iter = iter; this.rules = [{ getter: getter, desc: desc, compare: compare }] }, thenBy: function(getter, desc, compare) { var result = new SortIterator(this.sortedIter || this.iter, getter, desc, compare); if (!this.sortedIter) { result.rules = this.rules.concat(result.rules) } return result }, next: function() { this._ensureSorted(); return this.sortedIter.next() }, current: function() { this._ensureSorted(); return this.sortedIter.current() }, reset: function() { delete this.sortedIter }, countable: function() { return this.sortedIter || this.iter.countable() }, count: function() { if (this.sortedIter) { return this.sortedIter.count() } return this.iter.count() }, _ensureSorted: function() { var that = this; if (that.sortedIter) { return } each(that.rules, (function() { this.getter = compileGetter(this.getter) })); that.sortedIter = new MapIterator(new ArrayIterator(this.iter.toArray().sort((function(x, y) { return that._compare(x, y) }))), that._unwrap) }, _wrap: function(record, index) { return { index: index, value: record } }, _unwrap: function(wrappedItem) { return wrappedItem.value }, _compare: function(x, y) { var xIndex = x.index; var yIndex = y.index; x = x.value; y = y.value; if (x === y) { return xIndex - yIndex } for (var i = 0, rulesCount = this.rules.length; i < rulesCount; i++) { var rule = this.rules[i]; var xValue = rule.getter(x); var yValue = rule.getter(y); var compare = rule.compare || defaultCompare; var compareResult = compare(xValue, yValue); if (compareResult) { return rule.desc ? -compareResult : compareResult } } return xIndex - yIndex } }); var compileCriteria = function() { var toString = function(value) { return isDefined(value) ? value.toString() : "" }; function compileEquals(getter, value, negate) { return function(obj) { obj = toComparable(getter(obj)); var result = function(value) { return "" === value || 0 === value || false === value }(value) ? obj === value : obj == value; if (negate) { result = !result } return result } } return function(crit) { if (isFunction(crit)) { return crit } if (dataUtils.isGroupCriterion(crit)) { return function(crit) { var ops = []; var isConjunctiveOperator = false; var isConjunctiveNextOperator = false; each(crit, (function() { if (Array.isArray(this) || isFunction(this)) { if (ops.length > 1 && isConjunctiveOperator !== isConjunctiveNextOperator) { throw new errors.Error("E4019") } ops.push(compileCriteria(this)); isConjunctiveOperator = isConjunctiveNextOperator; isConjunctiveNextOperator = true } else { isConjunctiveNextOperator = dataUtils.isConjunctiveOperator(this) } })); return function(d) { var result = isConjunctiveOperator; for (var i = 0; i < ops.length; i++) { if (ops[i](d) !== isConjunctiveOperator) { result = !isConjunctiveOperator; break } } return result } }(crit) } if (dataUtils.isUnaryOperation(crit)) { return function(crit) { var op = crit[0]; var criteria = compileCriteria(crit[1]); if ("!" === op) { return function(obj) { return !criteria(obj) } } throw errors.Error("E4003", op) }(crit) } return function(crit) { crit = dataUtils.normalizeBinaryCriterion(crit); var getter = compileGetter(crit[0]); var op = crit[1]; var value = crit[2]; value = toComparable(value); switch (op.toLowerCase()) { case "=": return compileEquals(getter, value); case "<>": return compileEquals(getter, value, true); case ">": return function(obj) { return toComparable(getter(obj)) > value }; case "<": return function(obj) { return toComparable(getter(obj)) < value }; case ">=": return function(obj) { return toComparable(getter(obj)) >= value }; case "<=": return function(obj) { return toComparable(getter(obj)) <= value }; case "startswith": return function(obj) { return 0 === toComparable(toString(getter(obj))).indexOf(value) }; case "endswith": return function(obj) { var getterValue = toComparable(toString(getter(obj))); var searchValue = toString(value); if (getterValue.length < searchValue.length) { return false } var index = getterValue.lastIndexOf(value); return -1 !== index && index === getterValue.length - value.length }; case "contains": return function(obj) { return toComparable(toString(getter(obj))).indexOf(value) > -1 }; case "notcontains": return function(obj) { return -1 === toComparable(toString(getter(obj))).indexOf(value) } } throw errors.Error("E4003", op) }(crit) } }(); var FilterIterator = WrappedIterator.inherit({ ctor: function(iter, criteria) { this.callBase(iter); this.criteria = compileCriteria(criteria) }, next: function() { while (this.iter.next()) { if (this.criteria(this.current())) { return true } } return false } }); var GroupIterator = Iterator.inherit({ ctor: function(iter, getter) { this.iter = iter; this.getter = getter }, next: function() { this._ensureGrouped(); return this.groupedIter.next() }, current: function() { this._ensureGrouped(); return this.groupedIter.current() }, reset: function() { delete this.groupedIter }, countable: function() { return !!this.groupedIter }, count: function() { return this.groupedIter.count() }, _ensureGrouped: function() { if (this.groupedIter) { return } var hash = {}; var keys = []; var iter = this.iter; var getter = compileGetter(this.getter); iter.reset(); while (iter.next()) { var current = iter.current(); var key = getter(current); if (key in hash) { hash[key].push(current) } else { hash[key] = [current]; keys.push(key) } } this.groupedIter = new ArrayIterator(map(keys, (function(key) { return { key: key, items: hash[key] } }))) } }); var SelectIterator = WrappedIterator.inherit({ ctor: function(iter, getter) { this.callBase(iter); this.getter = compileGetter(getter) }, current: function() { return this.getter(this.callBase()) }, countable: function() { return this.iter.countable() }, count: function() { return this.iter.count() } }); var SliceIterator = WrappedIterator.inherit({ ctor: function(iter, skip, take) { this.callBase(iter); this.skip = Math.max(0, skip); this.take = Math.max(0, take); this.pos = 0 }, next: function() { if (this.pos >= this.skip + this.take) { return false } while (this.pos < this.skip && this.iter.next()) { this.pos++ } this.pos++; return this.iter.next() }, reset: function() { this.callBase(); this.pos = 0 }, countable: function() { return this.iter.countable() }, count: function() { return Math.min(this.iter.count() - this.skip, this.take) } }); var arrayQueryImpl = function arrayQueryImpl(iter, queryOptions) { queryOptions = queryOptions || {}; if (!(iter instanceof Iterator)) { iter = new ArrayIterator(iter) } var handleError = function(error) { var handler = queryOptions.errorHandler; if (handler) { handler(error) } handleDataError(error) }; var aggregateCore = function(aggregator) { var d = (new Deferred).fail(handleError); var seed; var step = aggregator.step; var finalize = aggregator.finalize; try { iter.reset(); if ("seed" in aggregator) { seed = aggregator.seed } else { seed = iter.next() ? iter.current() : NaN } var accumulator = seed; while (iter.next()) { accumulator = step(accumulator, iter.current()) } d.resolve(finalize ? finalize(accumulator) : accumulator) } catch (x) { d.reject(x) } return d.promise() }; var standardAggregate = function(name) { return aggregateCore(dataUtils.aggregators[name]) }; var select = function(getter) { if (!isFunction(getter) && !Array.isArray(getter)) { getter = [].slice.call(arguments) } return chainQuery(new SelectIterator(iter, getter)) }; var selectProp = function(name) { return select(compileGetter(name)) }; function chainQuery(iter) { return arrayQueryImpl(iter, queryOptions) } return { toArray: function() { return iter.toArray() }, enumerate: function() { var d = (new Deferred).fail(handleError); try { d.resolve(iter.toArray()) } catch (x) { d.reject(x) } return d.promise() }, sortBy: function(getter, desc, compare) { return chainQuery(new SortIterator(iter, getter, desc, compare)) }, thenBy: function(getter, desc, compare) { if (iter instanceof SortIterator) { return chainQuery(iter.thenBy(getter, desc, compare)) } throw errors.Error("E4004") }, filter: function(criteria) { if (!Array.isArray(criteria)) { criteria = [].slice.call(arguments) } return chainQuery(new FilterIterator(iter, criteria)) }, slice: function(skip, take) { if (void 0 === take) { take = Number.MAX_VALUE } return chainQuery(new SliceIterator(iter, skip, take)) }, select: select, groupBy: function(getter) { return chainQuery(new GroupIterator(iter, getter)) }, aggregate: function(seed, step, finalize) { if (arguments.length < 2) { return aggregateCore({ step: arguments[0] }) } return aggregateCore({ seed: seed, step: step, finalize: finalize }) }, count: function() { if (iter.countable()) { var d = (new Deferred).fail(handleError); try { d.resolve(iter.count()) } catch (x) { d.reject(x) } return d.promise() } return standardAggregate("count") }, sum: function(getter) { if (getter) { return selectProp(getter).sum() } return standardAggregate("sum") }, min: function(getter) { if (getter) { return selectProp(getter).min() } return standardAggregate("min") }, max: function(getter) { if (getter) { return selectProp(getter).max() } return standardAggregate("max") }, avg: function(getter) { if (getter) { return selectProp(getter).avg() } return standardAggregate("avg") } } }; export default arrayQueryImpl;