25'ten fazla konu seçemezsiniz Konular bir harf veya rakamla başlamalı, kısa çizgiler ('-') içerebilir ve en fazla 35 karakter uzunluğunda olabilir.

122 satır
3.2 KiB

  1. import {Collection} from './Collection.ts'
  2. export type MaybeIterationItem<T> = { done: boolean, value?: T }
  3. export type ChunkCallback<T> = (items: Collection<T>) => any
  4. export class StopIteration extends Error {}
  5. /**
  6. * Abstract class representing an iterable, lazy-loaded dataset.
  7. * @abstract
  8. */
  9. export abstract class Iterable<T> {
  10. /**
  11. * The current index of the iterable.
  12. * @type number
  13. */
  14. protected index = 0
  15. /**
  16. * Get the item of this iterable at the given index, if one exists.
  17. * @param {number} i
  18. * @return Promise<any|undefined>
  19. */
  20. abstract async at_index(i: number): Promise<T | undefined>
  21. /**
  22. * Get the collection of items in the given range of this iterable.
  23. * @param {number} start
  24. * @param {number} end
  25. * @return Promise<Collection>
  26. */
  27. abstract async from_range(start: number, end: number): Promise<Collection<T>>
  28. /**
  29. * Count the number of items in this collection.
  30. * @return Promise<number>
  31. */
  32. abstract async count(): Promise<number>
  33. /**
  34. * Get a copy of this iterable.
  35. * @return Iterable
  36. */
  37. abstract clone(): Iterable<T>
  38. /**
  39. * Advance to the next value of this iterable.
  40. * @return Promise<MaybeIterationItem>
  41. */
  42. public async next(): Promise<MaybeIterationItem<T>> {
  43. const i = this.index
  44. if ( i >= await this.count() ) {
  45. return { done: true }
  46. }
  47. this.index = i + 1
  48. return { done: false, value: await this.at_index(i) }
  49. }
  50. /**
  51. * Function to enable async iteration.
  52. *
  53. * @example
  54. * for await (const item of iterable) {}
  55. */
  56. [Symbol.asyncIterator]() {
  57. return this
  58. }
  59. /**
  60. * Chunk the iterable into the given size and call the callback passing the chunk along.
  61. * @param {number} size
  62. * @param {ChunkCallback} callback
  63. * @return Promise<void>
  64. */
  65. public async chunk(size: number, callback: ChunkCallback<T>) {
  66. const total = await this.count()
  67. while ( this.index < total ) {
  68. const items = await this.from_range(this.index, this.index + size - 1)
  69. try {
  70. await callback(items)
  71. } catch ( error ) {
  72. if ( error instanceof StopIteration ) break
  73. else throw error
  74. }
  75. this.index += size
  76. }
  77. }
  78. /**
  79. * Advance the iterable to the given index.
  80. * @param {number} index
  81. * @return Promise<void>
  82. */
  83. public async seek(index: number) {
  84. if ( index < 0 ) throw new TypeError('Cannot seek to negative index.')
  85. else if ( index >= await this.count() ) throw new TypeError('Cannot seek past last item.')
  86. this.index = index
  87. }
  88. /**
  89. * Peek at the next value of the iterable, without advancing.
  90. * @return Promise<any|undefined>
  91. */
  92. public async peek(): Promise<T | undefined> {
  93. if ( this.index + 1 >= await this.count() ) return undefined
  94. else return this.at_index(this.index + 1)
  95. }
  96. /**
  97. * Reset the iterable to the first index.
  98. * @return Promise<any>
  99. */
  100. public async reset() {
  101. this.index = 0
  102. }
  103. }