Advertisement
Guest User

Untitled

a guest
Jun 20th, 2019
90
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 9.32 KB | None | 0 0
  1. {
  2. // boundaries of list
  3. const FIRST = Symbol('first');
  4. const LAST = Symbol('last');
  5.  
  6. const isBefore = (head) => head == FIRST;
  7. const isNotBefore = (head) => !isBefore(head);
  8. const isAfter = (head) => head == LAST;
  9. const isNotAfter = (head) => !isAfter(head);
  10. const isOutside = (head) => isBefore(head) || isAfter(head);
  11. const isInside = (head) => head !== null && !isOutside(head);
  12.  
  13. const isEmpty = (start) => start === null;
  14. const isNotEmpty = (start) => !isEmpty(start);
  15.  
  16. // inner list utils
  17. const createCell = (data) => ({
  18. data,
  19. prev: null,
  20. next: null,
  21. });
  22.  
  23. const copyCell = ({ data }) => createCell(data);
  24.  
  25. const createList = (...items) => items.reduce(
  26. ({ latest, start }, current) => {
  27. const cell = createCell(current);
  28.  
  29. cell.next = LAST;
  30.  
  31. if (latest == null) {
  32. start = cell;
  33. cell.prev = FIRST;
  34. } else {
  35. cell.prev = latest;
  36. latest.next = cell;
  37. }
  38.  
  39. return { latest: cell, start };
  40. },
  41. { latest: null, start: null }
  42. );
  43.  
  44. const extractValuesFrom = (head, goForth = true) => {
  45. const conditionIsMet = goForth
  46. ? isNotAfter
  47. : isNotBefore;
  48. const STEP = goForth ? 'next' : 'prev';
  49.  
  50. const values = [];
  51.  
  52. for (let localHead = head[STEP]; conditionIsMet(localHead); localHead = localHead[STEP]) {
  53. values.push(localHead.data);
  54. }
  55.  
  56. if (!goForth) {
  57. values.reverse();
  58. }
  59.  
  60. return values;
  61. }
  62.  
  63. const copyList = (head) => {
  64. let start;
  65.  
  66. const listBefore = createList(...extractValuesFrom(head, false));
  67. const newHead = copyCell(head);
  68. const listAfter = createList(...extractValuesFrom(head));
  69.  
  70. if (listBefore.start) {
  71. start = listBefore.start;
  72. listBefore.latest.next = newHead;
  73. newHead.prev = listBefore.latest;
  74. } else {
  75. start = newHead;
  76. newHead.prev = FIRST;
  77. }
  78.  
  79. if (listAfter.start) {
  80. listAfter.start.prev = newHead;
  81. newHead.next = listAfter.start;
  82. } else {
  83. newHead.next = LAST;
  84. }
  85.  
  86. return {
  87. head: newHead,
  88. start,
  89. end: listAfter.start ? listAfter.latest : newHead,
  90. };
  91. };
  92.  
  93. // methods
  94. const withPrev = ({ api, head } = {}) => () => api(copyList(head.prev)); // `api` requires `linkedList`!
  95. const withNext = ({ api, head } = {}) => () => api(copyList(head.next)); // `api` requires `linkedList`!
  96.  
  97. const withClone = ({ start, linkedList }) => () => {
  98.  
  99. };
  100.  
  101. const withMap = ({ start, linkedList }) => (fn) => {
  102. if (typeof fn != 'function') {
  103. throw Error('Mapper has to be a function.');
  104. }
  105.  
  106. if (isEmpty(start)) {
  107. return null;
  108. }
  109.  
  110. const values = [];
  111.  
  112. for (let localHead = start; isInside(localHead); localHead = localHead.next) {
  113. values.push(fn(localHead.data));
  114. }
  115.  
  116. return linkedList(...values);
  117. };
  118.  
  119. const withFilter = ({ start, linkedList }) => (fn) => {
  120. if (typeof fn != 'function') {
  121. throw Error('Predicate has to be a function.');
  122. }
  123.  
  124. if (isEmpty(start)) {
  125. return null;
  126. }
  127.  
  128. const values = [];
  129.  
  130. for (let localHead = start; isInside(localHead); localHead = localHead.next) {
  131. if (fn(localHead.data)) {
  132. values.push(localHead.data);
  133. }
  134. }
  135.  
  136. return linkedList(...values);
  137. };
  138.  
  139. const withReduce = ({ start, linkedList }) => (...args) => {
  140. const [fn] = args;
  141.  
  142. if (typeof fn != 'function') {
  143. throw Error('Reducer has to be a function.');
  144. }
  145.  
  146. if (isEmpty(start)) {
  147. return null;
  148. }
  149.  
  150. let [localHead, value] = args.length == 1
  151. ? [start.next, start.data]
  152. : [start, args[1]];
  153.  
  154. for (/* above, for readability */; isInside(localHead); localHead = localHead.next) {
  155. value = fn(value, localHead.data)
  156. }
  157.  
  158. return value;
  159. };
  160.  
  161. const withInspect = ({ start, head } = {}) => () => {
  162. console.groupCollapsed('Full inspection');
  163.  
  164. const iterateOver = (start, fn) => {
  165. for (let localHead = start, index = 1; isInside(localHead); localHead = localHead.next, index += 1) {
  166. fn(index, localHead);
  167. }
  168. }
  169.  
  170. iterateOver(start, (index, localHead) => {
  171. console.log(`#${index}:`, localHead.data);
  172. })
  173.  
  174. iterateOver(start, (index, localHead) => {
  175. console.log(`#${index}:`, localHead);
  176. });
  177.  
  178. try {
  179. console.log(`Structure: ${JSON.stringify(start)}`);
  180. } catch(error) {
  181. console.info('Circular structure.');
  182. }
  183. console.log('start', start);
  184. console.log('head', head);
  185. if (start === null) {
  186. console.log('List is empty');
  187. } else {
  188. console.log('next', head.next);
  189. console.log('prev', head.prev);
  190. console.log('is head at start:', head == start);
  191. }
  192. console.groupEnd();
  193. };
  194.  
  195. const withIterator = ({ start } = {}) => function* ({ finite = false } = {}) {
  196. for (let localHead = start; isInside(localHead); localHead = localHead.next) {
  197. yield localHead.data;
  198.  
  199. if (finite && localHead == start) {
  200. break;
  201. }
  202. }
  203. };
  204.  
  205. // api builder
  206. const enumerableValue = (value, enumerable) => ({ value, enumerable });
  207. const enumerable = (value) => enumerableValue(value, true);
  208. const nonEnumerable = (value) => enumerableValue(value, false);
  209.  
  210. const api = ({ head, start, end, linkedList }) =>
  211. Object.defineProperties({}, {
  212. current: enumerable(isNotEmpty(start) && isInside(head)? head.data : null),
  213. isOutside: enumerable(isOutside(head)),
  214. isBefore: enumerable(isBefore(head)),
  215. isAfter: enumerable(isAfter(head)),
  216. isInside: enumerable(isInside(head)),
  217. isEmpty: enumerable(isEmpty(start)),
  218. prev: nonEnumerable(withPrev({ api, head })),
  219. next: nonEnumerable(withNext({ api, head })),
  220. // clone: withClone({ start, linkedList }),
  221. map: nonEnumerable(withMap({ start, linkedList })),
  222. filter: nonEnumerable(withFilter({ start, linkedList })),
  223. reduce: nonEnumerable(withReduce({ start, linkedList })),
  224. // reduceRight
  225. // concat
  226. // add + insert
  227. [Symbol.iterator]: nonEnumerable(withIterator({ start })),
  228. inspect: nonEnumerable(withInspect({ start, head })),
  229. });
  230.  
  231. // export
  232. const linkedList = (...items) => {
  233. const { start, start: head, latest: end } = createList(...items);
  234. return api({ head, start, end, linkedList });
  235. }
  236.  
  237. const LinkedList = {
  238. of: linkedList,
  239. };
  240.  
  241. // demos
  242. function demo(name, fn) {
  243. console.groupCollapsed(name);
  244. fn();
  245. console.groupEnd();
  246. }
  247.  
  248. function demo1() {
  249. // General demo
  250. console.log('LinkedList.of(1, 2, 3, 4, 5, 6)');
  251. const list1 = LinkedList.of(1, 2, 3, 4, 5, 6)
  252. console.log(list1);
  253. list1.inspect();
  254. console.groupEnd();
  255. }
  256.  
  257. function demo2() {
  258. // Iterator demo
  259. console.info('const list2 = LinkedList.of(1987, 1988, 1989, 1990, 1991, 1992);')
  260. const list2 = LinkedList.of(1987, 1988, 1989, 1990, 1991, 1992);
  261. console.log(list2);
  262. console.info('[...list2]');
  263. console.log([...list2])
  264. }
  265.  
  266. function demo3() {
  267. // Next prev
  268. console.info('const list2 = LinkedList.of(1999, 2000, 2001, 2002, 2003, 2004);')
  269. const list3 = LinkedList.of(1999, 2000, 2001, 2002, 2003, 2004);
  270. console.log(list3);
  271. console.log('list3.current', list3.current);
  272. console.log('list3.next().current', list3.next().current);
  273. console.log('list3.next().current', list3.next().current);
  274. console.log('list3.next().next().current', list3.next().next().current);
  275. console.log('list3.next().current == list3.next().current', list3.next().current == list3.next().current);
  276. console.log('list3.next() == list3.next()', list3.next() == list3.next());
  277. }
  278.  
  279. function demo4() {
  280. // Empty list
  281. const list4 = LinkedList.of();
  282. console.info('const list4 = LinkedList.of();');
  283. console.log(list4);
  284. console.log('list4.isEmpty', list4.isEmpty);
  285. list4.inspect();
  286. }
  287.  
  288. function demo5() {
  289. // Map / filter / reduce
  290. const list5 = LinkedList.of(1, 2, 3, 4, 5, 6);
  291. console.info('const list5 = LinkedList.of(1, 2, 3, 4, 5, 6);');
  292. console.log('Original', [...list5]);
  293. console.log('Mapped (doubled)', [...list5.map((value) => value * 2)]);
  294. console.log('Filtered (odd)', [...list5.filter((value) => value % 2)]);
  295. console.log('Reduced (summed)', list5.reduce((a, b) => a + b, 0));
  296. }
  297.  
  298. // main
  299. console.clear();
  300.  
  301. // demo('General demo', demo1);
  302. // demo('Iterator demo', demo2);
  303. // demo('Prev/next demo', demo3);
  304. // demo('Empty list', demo4);
  305. demo('Map / filter functions', demo5);
  306.  
  307. console.info('The end.')
  308. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement