toolkit.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533
  1. /**
  2. * Tool Functions
  3. *
  4. * Note that some of these functions return an iterator rather than an array,
  5. * which is intentional to make chain operations more efficient.
  6. */
  7. const ALPHABET = 'abcdefghijklmnopqrstuvwxyz'
  8. const NotFound = { tip: 'Object Not Found' }
  9. const CR = '\r'
  10. const LF = '\n'
  11. const TAB = '\t'
  12. /**
  13. * Checks if `object[key]` exists as an own property of `object`
  14. *
  15. * @param key string | symbol
  16. * @param object object
  17. * @return boolean
  18. */
  19. function has (key, object) {
  20. assert(typeof key == 'string' || typeof key == 'symbol')
  21. assert(typeof object == 'object' && object !== null)
  22. return Object.prototype.hasOwnProperty.call(object, key)
  23. }
  24. /**
  25. * Simple substitution of `object.__proto__`
  26. *
  27. * @param object object
  28. * @return object
  29. */
  30. function get_proto (object) {
  31. assert(typeof object == 'object')
  32. return Object.getPrototypeOf(object)
  33. }
  34. /**
  35. * Shorthand of `Object.assign`
  36. *
  37. * @param o1 object
  38. * @param o2 object
  39. * @return object (o1)
  40. */
  41. function pour (o1, o2) {
  42. assert(typeof o1 == 'object' && o1 !== null)
  43. assert(typeof o2 == 'object' && o2 !== null)
  44. return Object.assign(o1, o2)
  45. }
  46. /**
  47. * Converts iterable object to array
  48. *
  49. * @param iterable iterable
  50. * @return array
  51. */
  52. function list (iterable) {
  53. let result = []
  54. for (let I of iterable) {
  55. result.push(I)
  56. }
  57. return result
  58. }
  59. /**
  60. * Creates reversed iterator for array
  61. *
  62. * @param list array
  63. * @return iterator
  64. */
  65. function *rev (list) {
  66. assert(list instanceof Array)
  67. for (let i=list.length-1; i>=0; i--) {
  68. yield list[i]
  69. }
  70. }
  71. /**
  72. * Similar to Array.prototype.map, but maps iterable into iterator
  73. *
  74. * @param iterable iterable
  75. * @return iterator
  76. */
  77. function *map (iterable, f) {
  78. let index = 0
  79. for (let I of iterable) {
  80. yield f(I, index)
  81. index += 1
  82. }
  83. }
  84. /**
  85. * Takes until the nth element of given iterable
  86. *
  87. * @param iterable iterable
  88. * @param n integer
  89. * @return iterator
  90. */
  91. function *take (iterable, n) {
  92. assert(Number.isSafeInteger(n))
  93. let index = 0
  94. for (let I of iterable) {
  95. if (index < n) {
  96. yield I
  97. index += 1
  98. } else {
  99. break
  100. }
  101. }
  102. }
  103. /**
  104. * Performs a parallel merge on an array of iterable objects
  105. *
  106. * @param it_list iterable[]
  107. * @param f function
  108. * @return iterator
  109. */
  110. function *zip (it_list, f) {
  111. assert(it_list instanceof Array)
  112. assert(typeof f == 'function')
  113. let iterators = it_list.map(iterable => iterable[Symbol.iterator]())
  114. while (true) {
  115. let results = iterators.map(it => it.next())
  116. if (exists(results, r => r.done)) {
  117. break
  118. } else {
  119. yield f(results.map(r => r.value))
  120. }
  121. }
  122. }
  123. /**
  124. * Creates a new hash table with keys from `object` mapped by `f`
  125. *
  126. * @param object object
  127. * @param f function
  128. * @return object
  129. */
  130. function mapkey (object, f) {
  131. assert(object instanceof object)
  132. assert(typeof f == 'function')
  133. let mapped = {}
  134. for (let key of Object.keys(object)) {
  135. let value = object[key]
  136. mapped[f(key, value)] = value
  137. }
  138. return mapped
  139. }
  140. /**
  141. * Creates a new hash table with values from `object` mapped by `f`
  142. *
  143. * @param object object
  144. * @param f function
  145. * @return object
  146. */
  147. function mapval (object, f) {
  148. let mapped = {}
  149. for (let key of Object.keys(object)) {
  150. mapped[key] = f(object[key], key)
  151. }
  152. return mapped
  153. }
  154. /**
  155. * Creates an iterator with elements mapped from `object` as `f(key, value)`
  156. *
  157. * @param object object
  158. * @param f function
  159. * @return iterator
  160. */
  161. function *mapkv (object, f) {
  162. for (let key of Object.keys(object)) {
  163. yield f(key, object[key])
  164. }
  165. }
  166. /**
  167. * Performs a shallow copy of an array or a hash table
  168. *
  169. * @param object array | object
  170. * @return array | object
  171. */
  172. function copy (object) {
  173. if (object instanceof Array) {
  174. return list(map(object, x => x))
  175. } else {
  176. assert(typeof object == 'object')
  177. return mapval(object, x => x)
  178. }
  179. }
  180. /**
  181. * Performs a shallow equality test on a pair of arrays or hash tables
  182. *
  183. * @param o1 array | object
  184. * @param o2 array | object
  185. * @param cmp function?
  186. * @return boolean
  187. */
  188. function equal (o1, o2, cmp = (x, y) => (x === y)) {
  189. if (o1 instanceof Array && o2 instanceof Array) {
  190. return (
  191. o1.length == o2.length
  192. && forall(o1, (e,i) => cmp(e, o2[i]))
  193. )
  194. } else {
  195. assert(typeof o1 == 'object' && typeof o2 == 'object')
  196. let k1 = Object.keys(o1)
  197. let k2 = Object.keys(o2)
  198. return (
  199. k1.length == k2.length
  200. && forall(k1, k => has(k,o2) && cmp(o1[k], o2[k]))
  201. )
  202. }
  203. }
  204. /**
  205. * Performs a shallow equality test on two sets
  206. *
  207. * @param s1 set
  208. * @param s2 set
  209. * @return boolean
  210. */
  211. function set_equal (s1, s2) {
  212. assert(s1 instanceof Set)
  213. assert(s2 instanceof Set)
  214. if (s1.size != s2.size) { return false }
  215. return forall(s1, e => s2.has(e))
  216. }
  217. /**
  218. * Apply `f` on elements of an iterable object or entries of a hash table
  219. *
  220. * @param something iterable | object
  221. * @param f function
  222. */
  223. function foreach (something, f) {
  224. if (typeof something[Symbol.iterator] == 'function') {
  225. let i = 0
  226. for (let I of something) {
  227. f(I, i)
  228. i += 1
  229. }
  230. } else {
  231. assert(typeof something == 'object')
  232. for (let key of Object.keys(something)) {
  233. f(key, something[key])
  234. }
  235. }
  236. }
  237. /**
  238. * Similar to Array.prototype.filter but filters iterable into iterator
  239. *
  240. * @param iterable iterable
  241. * @param f function
  242. * @return iterator
  243. */
  244. function *filter (iterable, f) {
  245. let index = 0
  246. for (let I of iterable) {
  247. if (f(I, index)) {
  248. yield I
  249. }
  250. index += 1
  251. }
  252. }
  253. /**
  254. * Creates a new hash table with keys and values from `object` filtered by `f`
  255. *
  256. * @param object object
  257. * @param f function
  258. * @return object
  259. */
  260. function flkv (object, f) {
  261. let result = {}
  262. for (let key of Object.keys(object)) {
  263. if (f(key, object[key])) {
  264. result[key] = object[key]
  265. }
  266. }
  267. return result
  268. }
  269. /**
  270. * Concatenates `iterables`
  271. *
  272. * @param ...iterables iterable[]
  273. * @return iterator
  274. */
  275. function *cat (...iterables) {
  276. for (let iterable of iterables) {
  277. for (let I of iterable) {
  278. yield I
  279. }
  280. }
  281. }
  282. /**
  283. * Flattens an iterable of iterable objects
  284. *
  285. * @param iterable_of_iterable iterable
  286. * @return iterator
  287. */
  288. function *flat (iterable_of_iterable) {
  289. for (let iterable of iterable_of_iterable) {
  290. for (let I of iterable) {
  291. yield I
  292. }
  293. }
  294. }
  295. /**
  296. * Similar to Array.prototype.join but creates string from iterable object
  297. *
  298. * @param iterable iterable
  299. * @return string
  300. */
  301. function join (iterable, separator) {
  302. let string = ''
  303. let first = true
  304. for (let I of iterable) {
  305. if (first) {
  306. first = false
  307. } else {
  308. string += separator
  309. }
  310. string += I
  311. }
  312. return string
  313. }
  314. /**
  315. * Looks for an element in `iterable` that makes f(element) a truthy value,
  316. * returns `NotFound` when such element does not exist
  317. *
  318. * @param iterable iterable
  319. * @param f function
  320. * @return any
  321. */
  322. function find (iterable, f) {
  323. let index = 0
  324. for (let I of iterable) {
  325. if (f(I, index)) {
  326. return I
  327. }
  328. index += 1
  329. }
  330. return NotFound
  331. }
  332. /**
  333. * Evaluate `next_of(next_of(...(next_of(initial))))` until terminate
  334. * condition is satisfied
  335. *
  336. * @param initial any
  337. * @param next_of function
  338. * @return iterator
  339. */
  340. function *iterate (initial, next_of, terminate) {
  341. // apply next_of() on value until terminal condition satisfied
  342. let value = initial
  343. while (!terminate(value)) {
  344. yield value
  345. value = next_of(value)
  346. }
  347. }
  348. /**
  349. * Similar to Array.prototype.reduce but reduces iterable object
  350. *
  351. * @param iterable iterable
  352. * @param initial any
  353. * @param f function
  354. * @param terminate function
  355. * @return any
  356. */
  357. function fold (iterable, initial, f, terminate) {
  358. // reduce() with a terminal condition
  359. let index = 0
  360. let value = initial
  361. for (let I of iterable) {
  362. value = f(I, value, index)
  363. index += 1
  364. if (terminate && terminate(value)) {
  365. break
  366. }
  367. }
  368. return value
  369. }
  370. /**
  371. * Tests if ∀ I ∈ iterable, f(I) === true
  372. *
  373. * @param iterable iterable
  374. * @param f function (must return boolean value)
  375. * @return boolean
  376. */
  377. function forall (iterable, f) {
  378. return fold(
  379. iterable, true, ((e,v,i) => v && f(e,i)), (v => {
  380. assert(typeof v == 'boolean')
  381. return (v == false)
  382. })
  383. )
  384. }
  385. /**
  386. * Tests if ∃ I ∈ iterable, f(I) === true
  387. *
  388. * @param iterable iterable
  389. * @param f function (must return boolean value)
  390. * @return boolean
  391. */
  392. function exists (iterable, f) {
  393. return fold(
  394. iterable, false, ((e,v,i) => v || f(e,i)), (v => {
  395. assert(typeof v == 'boolean')
  396. return (v == true)
  397. })
  398. )
  399. }
  400. /**
  401. * Composite `functions`
  402. *
  403. * @param functions function[]
  404. * @return function
  405. */
  406. function chain (functions) {
  407. assert(functions instanceof Array)
  408. assert(functions.every(f => typeof f == 'function'))
  409. return ( x => fold(functions, x, (f, v) => f(v)) )
  410. }
  411. /**
  412. * Count from 0 to n-1
  413. *
  414. * @param n integer
  415. * @return iterator
  416. */
  417. function *count (n) {
  418. assert(Number.isSafeInteger(n))
  419. let i = 0
  420. while (i < n) {
  421. yield i
  422. i += 1
  423. }
  424. }
  425. /**
  426. * Checks if there is no repeated element in the `iterable`
  427. *
  428. * @param iterable iterable
  429. * @return boolean
  430. */
  431. function no_repeat (iterable) {
  432. let s = new Set()
  433. for (let I of iterable) {
  434. if (!s.has(I)) {
  435. s.add(I)
  436. } else {
  437. return false
  438. }
  439. }
  440. return true
  441. }
  442. /**
  443. * Wraps function with specified arity given to it
  444. *
  445. * @param f function
  446. * @param n integer
  447. * @return function
  448. */
  449. function give_arity(f, n) {
  450. assert(Number.isSafeInteger(n))
  451. assert(0 <= n && n <= ALPHABET.length)
  452. let para_list = join(filter(ALPHABET, (e,i) => i < n), ',')
  453. let g = new Function(para_list, 'return this.apply(null, arguments)')
  454. return g.bind(f)
  455. }
  456. /**
  457. * Extracts a summary of `string`
  458. *
  459. * @param string string
  460. * @param n integer
  461. * @return string
  462. */
  463. function get_summary (string, n = 60) {
  464. assert(typeof string == 'string')
  465. assert(Number.isSafeInteger(n))
  466. let t = string.substring(0, n).replace(/[\n \t]+/g, ' ').trim()
  467. if (string.length <= n) {
  468. return t
  469. } else {
  470. return (t + '...')
  471. }
  472. }