array_query.js 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560
  1. /**
  2. * DevExtreme (data/array_query.js)
  3. * Version: 19.1.16
  4. * Build date: Tue Oct 18 2022
  5. *
  6. * Copyright (c) 2012 - 2022 Developer Express Inc. ALL RIGHTS RESERVED
  7. * Read about DevExtreme licensing here: https://js.devexpress.com/Licensing/
  8. */
  9. "use strict";
  10. var Class = require("../core/class");
  11. var typeUtils = require("../core/utils/type");
  12. var iteratorUtils = require("../core/utils/iterator");
  13. var compileGetter = require("../core/utils/data").compileGetter;
  14. var toComparable = require("../core/utils/data").toComparable;
  15. var Deferred = require("../core/utils/deferred").Deferred;
  16. var errorsModule = require("./errors");
  17. var dataUtils = require("./utils");
  18. var Iterator = Class.inherit({
  19. toArray: function() {
  20. var result = [];
  21. this.reset();
  22. while (this.next()) {
  23. result.push(this.current())
  24. }
  25. return result
  26. },
  27. countable: function() {
  28. return false
  29. }
  30. });
  31. var ArrayIterator = Iterator.inherit({
  32. ctor: function(array) {
  33. this.array = array;
  34. this.index = -1
  35. },
  36. next: function() {
  37. if (this.index + 1 < this.array.length) {
  38. this.index++;
  39. return true
  40. }
  41. return false
  42. },
  43. current: function() {
  44. return this.array[this.index]
  45. },
  46. reset: function() {
  47. this.index = -1
  48. },
  49. toArray: function() {
  50. return this.array.slice(0)
  51. },
  52. countable: function() {
  53. return true
  54. },
  55. count: function() {
  56. return this.array.length
  57. }
  58. });
  59. var WrappedIterator = Iterator.inherit({
  60. ctor: function(iter) {
  61. this.iter = iter
  62. },
  63. next: function() {
  64. return this.iter.next()
  65. },
  66. current: function() {
  67. return this.iter.current()
  68. },
  69. reset: function() {
  70. return this.iter.reset()
  71. }
  72. });
  73. var MapIterator = WrappedIterator.inherit({
  74. ctor: function(iter, mapper) {
  75. this.callBase(iter);
  76. this.index = -1;
  77. this.mapper = mapper
  78. },
  79. current: function() {
  80. return this.mapper(this.callBase(), this.index)
  81. },
  82. next: function() {
  83. var hasNext = this.callBase();
  84. if (hasNext) {
  85. this.index++
  86. }
  87. return hasNext
  88. }
  89. });
  90. var defaultCompare = function(xValue, yValue) {
  91. xValue = toComparable(xValue);
  92. yValue = toComparable(yValue);
  93. if (null === xValue && null !== yValue) {
  94. return -1
  95. }
  96. if (null !== xValue && null === yValue) {
  97. return 1
  98. }
  99. if (void 0 === xValue && void 0 !== yValue) {
  100. return 1
  101. }
  102. if (void 0 !== xValue && void 0 === yValue) {
  103. return -1
  104. }
  105. if (xValue < yValue) {
  106. return -1
  107. }
  108. if (xValue > yValue) {
  109. return 1
  110. }
  111. return 0
  112. };
  113. var SortIterator = Iterator.inherit({
  114. ctor: function(iter, getter, desc, compare) {
  115. if (!(iter instanceof MapIterator)) {
  116. iter = new MapIterator(iter, this._wrap)
  117. }
  118. this.iter = iter;
  119. this.rules = [{
  120. getter: getter,
  121. desc: desc,
  122. compare: compare
  123. }]
  124. },
  125. thenBy: function(getter, desc, compare) {
  126. var result = new SortIterator(this.sortedIter || this.iter, getter, desc, compare);
  127. if (!this.sortedIter) {
  128. result.rules = this.rules.concat(result.rules)
  129. }
  130. return result
  131. },
  132. next: function() {
  133. this._ensureSorted();
  134. return this.sortedIter.next()
  135. },
  136. current: function() {
  137. this._ensureSorted();
  138. return this.sortedIter.current()
  139. },
  140. reset: function() {
  141. delete this.sortedIter
  142. },
  143. countable: function() {
  144. return this.sortedIter || this.iter.countable()
  145. },
  146. count: function() {
  147. if (this.sortedIter) {
  148. return this.sortedIter.count()
  149. }
  150. return this.iter.count()
  151. },
  152. _ensureSorted: function() {
  153. var that = this;
  154. if (that.sortedIter) {
  155. return
  156. }
  157. iteratorUtils.each(that.rules, function() {
  158. this.getter = compileGetter(this.getter)
  159. });
  160. that.sortedIter = new MapIterator(new ArrayIterator(this.iter.toArray().sort(function(x, y) {
  161. return that._compare(x, y)
  162. })), that._unwrap)
  163. },
  164. _wrap: function(record, index) {
  165. return {
  166. index: index,
  167. value: record
  168. }
  169. },
  170. _unwrap: function(wrappedItem) {
  171. return wrappedItem.value
  172. },
  173. _compare: function(x, y) {
  174. var xIndex = x.index;
  175. var yIndex = y.index;
  176. x = x.value;
  177. y = y.value;
  178. if (x === y) {
  179. return xIndex - yIndex
  180. }
  181. for (var i = 0, rulesCount = this.rules.length; i < rulesCount; i++) {
  182. var rule = this.rules[i];
  183. var xValue = rule.getter(x);
  184. var yValue = rule.getter(y);
  185. var compare = rule.compare || defaultCompare;
  186. var compareResult = compare(xValue, yValue);
  187. if (compareResult) {
  188. return rule.desc ? -compareResult : compareResult
  189. }
  190. }
  191. return xIndex - yIndex
  192. }
  193. });
  194. var compileCriteria = function() {
  195. var compileGroup = function(crit) {
  196. var ops = [];
  197. var isConjunctiveOperator = false;
  198. var isConjunctiveNextOperator = false;
  199. iteratorUtils.each(crit, function() {
  200. if (Array.isArray(this) || typeUtils.isFunction(this)) {
  201. if (ops.length > 1 && isConjunctiveOperator !== isConjunctiveNextOperator) {
  202. throw new errorsModule.errors.Error("E4019")
  203. }
  204. ops.push(compileCriteria(this));
  205. isConjunctiveOperator = isConjunctiveNextOperator;
  206. isConjunctiveNextOperator = true
  207. } else {
  208. isConjunctiveNextOperator = dataUtils.isConjunctiveOperator(this)
  209. }
  210. });
  211. return function(d) {
  212. var result = isConjunctiveOperator;
  213. for (var i = 0; i < ops.length; i++) {
  214. if (ops[i](d) !== isConjunctiveOperator) {
  215. result = !isConjunctiveOperator;
  216. break
  217. }
  218. }
  219. return result
  220. }
  221. };
  222. var toString = function(value) {
  223. return typeUtils.isDefined(value) ? value.toString() : ""
  224. };
  225. var compileBinary = function(crit) {
  226. crit = dataUtils.normalizeBinaryCriterion(crit);
  227. var getter = compileGetter(crit[0]);
  228. var op = crit[1];
  229. var value = crit[2];
  230. value = toComparable(value);
  231. switch (op.toLowerCase()) {
  232. case "=":
  233. return compileEquals(getter, value);
  234. case "<>":
  235. return compileEquals(getter, value, true);
  236. case ">":
  237. return function(obj) {
  238. return toComparable(getter(obj)) > value
  239. };
  240. case "<":
  241. return function(obj) {
  242. return toComparable(getter(obj)) < value
  243. };
  244. case ">=":
  245. return function(obj) {
  246. return toComparable(getter(obj)) >= value
  247. };
  248. case "<=":
  249. return function(obj) {
  250. return toComparable(getter(obj)) <= value
  251. };
  252. case "startswith":
  253. return function(obj) {
  254. return 0 === toComparable(toString(getter(obj))).indexOf(value)
  255. };
  256. case "endswith":
  257. return function(obj) {
  258. var getterValue = toComparable(toString(getter(obj)));
  259. var searchValue = toString(value);
  260. if (getterValue.length < searchValue.length) {
  261. return false
  262. }
  263. var index = getterValue.lastIndexOf(value);
  264. return index !== -1 && index === getterValue.length - value.length
  265. };
  266. case "contains":
  267. return function(obj) {
  268. return toComparable(toString(getter(obj))).indexOf(value) > -1
  269. };
  270. case "notcontains":
  271. return function(obj) {
  272. return toComparable(toString(getter(obj))).indexOf(value) === -1
  273. }
  274. }
  275. throw errorsModule.errors.Error("E4003", op)
  276. };
  277. function compileEquals(getter, value, negate) {
  278. return function(obj) {
  279. obj = toComparable(getter(obj));
  280. var result = useStrictComparison(value) ? obj === value : obj == value;
  281. if (negate) {
  282. result = !result
  283. }
  284. return result
  285. }
  286. }
  287. function useStrictComparison(value) {
  288. return "" === value || 0 === value || false === value
  289. }
  290. function compileUnary(crit) {
  291. var op = crit[0];
  292. var criteria = compileCriteria(crit[1]);
  293. if ("!" === op) {
  294. return function(obj) {
  295. return !criteria(obj)
  296. }
  297. }
  298. throw errorsModule.errors.Error("E4003", op)
  299. }
  300. return function(crit) {
  301. if (typeUtils.isFunction(crit)) {
  302. return crit
  303. }
  304. if (dataUtils.isGroupCriterion(crit)) {
  305. return compileGroup(crit)
  306. }
  307. if (dataUtils.isUnaryOperation(crit)) {
  308. return compileUnary(crit)
  309. }
  310. return compileBinary(crit)
  311. }
  312. }();
  313. var FilterIterator = WrappedIterator.inherit({
  314. ctor: function(iter, criteria) {
  315. this.callBase(iter);
  316. this.criteria = compileCriteria(criteria)
  317. },
  318. next: function() {
  319. while (this.iter.next()) {
  320. if (this.criteria(this.current())) {
  321. return true
  322. }
  323. }
  324. return false
  325. }
  326. });
  327. var GroupIterator = Iterator.inherit({
  328. ctor: function(iter, getter) {
  329. this.iter = iter;
  330. this.getter = getter
  331. },
  332. next: function() {
  333. this._ensureGrouped();
  334. return this.groupedIter.next()
  335. },
  336. current: function() {
  337. this._ensureGrouped();
  338. return this.groupedIter.current()
  339. },
  340. reset: function() {
  341. delete this.groupedIter
  342. },
  343. countable: function() {
  344. return !!this.groupedIter
  345. },
  346. count: function() {
  347. return this.groupedIter.count()
  348. },
  349. _ensureGrouped: function() {
  350. if (this.groupedIter) {
  351. return
  352. }
  353. var hash = {};
  354. var keys = [];
  355. var iter = this.iter;
  356. var getter = compileGetter(this.getter);
  357. iter.reset();
  358. while (iter.next()) {
  359. var current = iter.current();
  360. var key = getter(current);
  361. if (key in hash) {
  362. hash[key].push(current)
  363. } else {
  364. hash[key] = [current];
  365. keys.push(key)
  366. }
  367. }
  368. this.groupedIter = new ArrayIterator(iteratorUtils.map(keys, function(key) {
  369. return {
  370. key: key,
  371. items: hash[key]
  372. }
  373. }))
  374. }
  375. });
  376. var SelectIterator = WrappedIterator.inherit({
  377. ctor: function(iter, getter) {
  378. this.callBase(iter);
  379. this.getter = compileGetter(getter)
  380. },
  381. current: function() {
  382. return this.getter(this.callBase())
  383. },
  384. countable: function() {
  385. return this.iter.countable()
  386. },
  387. count: function() {
  388. return this.iter.count()
  389. }
  390. });
  391. var SliceIterator = WrappedIterator.inherit({
  392. ctor: function(iter, skip, take) {
  393. this.callBase(iter);
  394. this.skip = Math.max(0, skip);
  395. this.take = Math.max(0, take);
  396. this.pos = 0
  397. },
  398. next: function() {
  399. if (this.pos >= this.skip + this.take) {
  400. return false
  401. }
  402. while (this.pos < this.skip && this.iter.next()) {
  403. this.pos++
  404. }
  405. this.pos++;
  406. return this.iter.next()
  407. },
  408. reset: function() {
  409. this.callBase();
  410. this.pos = 0
  411. },
  412. countable: function() {
  413. return this.iter.countable()
  414. },
  415. count: function() {
  416. return Math.min(this.iter.count() - this.skip, this.take)
  417. }
  418. });
  419. var arrayQueryImpl = function arrayQueryImpl(iter, queryOptions) {
  420. queryOptions = queryOptions || {};
  421. if (!(iter instanceof Iterator)) {
  422. iter = new ArrayIterator(iter)
  423. }
  424. var handleError = function(error) {
  425. var handler = queryOptions.errorHandler;
  426. if (handler) {
  427. handler(error)
  428. }
  429. errorsModule._errorHandler(error)
  430. };
  431. var aggregateCore = function(aggregator) {
  432. var d = (new Deferred).fail(handleError);
  433. var seed;
  434. var step = aggregator.step;
  435. var finalize = aggregator.finalize;
  436. try {
  437. iter.reset();
  438. if ("seed" in aggregator) {
  439. seed = aggregator.seed
  440. } else {
  441. seed = iter.next() ? iter.current() : NaN
  442. }
  443. var accumulator = seed;
  444. while (iter.next()) {
  445. accumulator = step(accumulator, iter.current())
  446. }
  447. d.resolve(finalize ? finalize(accumulator) : accumulator)
  448. } catch (x) {
  449. d.reject(x)
  450. }
  451. return d.promise()
  452. };
  453. var aggregate = function(seed, step, finalize) {
  454. if (arguments.length < 2) {
  455. return aggregateCore({
  456. step: arguments[0]
  457. })
  458. }
  459. return aggregateCore({
  460. seed: seed,
  461. step: step,
  462. finalize: finalize
  463. })
  464. };
  465. var standardAggregate = function(name) {
  466. return aggregateCore(dataUtils.aggregators[name])
  467. };
  468. var select = function(getter) {
  469. if (!typeUtils.isFunction(getter) && !Array.isArray(getter)) {
  470. getter = [].slice.call(arguments)
  471. }
  472. return chainQuery(new SelectIterator(iter, getter))
  473. };
  474. var selectProp = function(name) {
  475. return select(compileGetter(name))
  476. };
  477. var chainQuery = function(iter) {
  478. return arrayQueryImpl(iter, queryOptions)
  479. };
  480. return {
  481. toArray: function() {
  482. return iter.toArray()
  483. },
  484. enumerate: function() {
  485. var d = (new Deferred).fail(handleError);
  486. try {
  487. d.resolve(iter.toArray())
  488. } catch (x) {
  489. d.reject(x)
  490. }
  491. return d.promise()
  492. },
  493. sortBy: function(getter, desc, compare) {
  494. return chainQuery(new SortIterator(iter, getter, desc, compare))
  495. },
  496. thenBy: function(getter, desc, compare) {
  497. if (iter instanceof SortIterator) {
  498. return chainQuery(iter.thenBy(getter, desc, compare))
  499. }
  500. throw errorsModule.errors.Error("E4004")
  501. },
  502. filter: function(criteria) {
  503. if (!Array.isArray(criteria)) {
  504. criteria = [].slice.call(arguments)
  505. }
  506. return chainQuery(new FilterIterator(iter, criteria))
  507. },
  508. slice: function(skip, take) {
  509. if (void 0 === take) {
  510. take = Number.MAX_VALUE
  511. }
  512. return chainQuery(new SliceIterator(iter, skip, take))
  513. },
  514. select: select,
  515. groupBy: function(getter) {
  516. return chainQuery(new GroupIterator(iter, getter))
  517. },
  518. aggregate: aggregate,
  519. count: function() {
  520. if (iter.countable()) {
  521. var d = (new Deferred).fail(handleError);
  522. try {
  523. d.resolve(iter.count())
  524. } catch (x) {
  525. d.reject(x)
  526. }
  527. return d.promise()
  528. }
  529. return standardAggregate("count")
  530. },
  531. sum: function(getter) {
  532. if (getter) {
  533. return selectProp(getter).sum()
  534. }
  535. return standardAggregate("sum")
  536. },
  537. min: function(getter) {
  538. if (getter) {
  539. return selectProp(getter).min()
  540. }
  541. return standardAggregate("min")
  542. },
  543. max: function(getter) {
  544. if (getter) {
  545. return selectProp(getter).max()
  546. }
  547. return standardAggregate("max")
  548. },
  549. avg: function(getter) {
  550. if (getter) {
  551. return selectProp(getter).avg()
  552. }
  553. return standardAggregate("avg")
  554. }
  555. }
  556. };
  557. module.exports = arrayQueryImpl;