Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- {
- // boundaries of list
- const FIRST = Symbol('first');
- const LAST = Symbol('last');
- const isBefore = (head) => head == FIRST;
- const isNotBefore = (head) => !isBefore(head);
- const isAfter = (head) => head == LAST;
- const isNotAfter = (head) => !isAfter(head);
- const isOutside = (head) => isBefore(head) || isAfter(head);
- const isInside = (head) => head !== null && !isOutside(head);
- const isEmpty = (start) => start === null;
- const isNotEmpty = (start) => !isEmpty(start);
- // inner list utils
- const createCell = (data) => ({
- data,
- prev: null,
- next: null,
- });
- const copyCell = ({ data }) => createCell(data);
- const createList = (...items) => items.reduce(
- ({ latest, start }, current) => {
- const cell = createCell(current);
- cell.next = LAST;
- if (latest == null) {
- start = cell;
- cell.prev = FIRST;
- } else {
- cell.prev = latest;
- latest.next = cell;
- }
- return { latest: cell, start };
- },
- { latest: null, start: null }
- );
- const extractValuesFrom = (head, goForth = true) => {
- const conditionIsMet = goForth
- ? isNotAfter
- : isNotBefore;
- const STEP = goForth ? 'next' : 'prev';
- const values = [];
- for (let localHead = head[STEP]; conditionIsMet(localHead); localHead = localHead[STEP]) {
- values.push(localHead.data);
- }
- if (!goForth) {
- values.reverse();
- }
- return values;
- }
- const copyList = (head) => {
- let start;
- const listBefore = createList(...extractValuesFrom(head, false));
- const newHead = copyCell(head);
- const listAfter = createList(...extractValuesFrom(head));
- if (listBefore.start) {
- start = listBefore.start;
- listBefore.latest.next = newHead;
- newHead.prev = listBefore.latest;
- } else {
- start = newHead;
- newHead.prev = FIRST;
- }
- if (listAfter.start) {
- listAfter.start.prev = newHead;
- newHead.next = listAfter.start;
- } else {
- newHead.next = LAST;
- }
- return {
- head: newHead,
- start,
- end: listAfter.start ? listAfter.latest : newHead,
- };
- };
- // methods
- const withPrev = ({ api, head } = {}) => () => api(copyList(head.prev)); // `api` requires `linkedList`!
- const withNext = ({ api, head } = {}) => () => api(copyList(head.next)); // `api` requires `linkedList`!
- const withClone = ({ start, linkedList }) => () => {
- };
- const withMap = ({ start, linkedList }) => (fn) => {
- if (typeof fn != 'function') {
- throw Error('Mapper has to be a function.');
- }
- if (isEmpty(start)) {
- return null;
- }
- const values = [];
- for (let localHead = start; isInside(localHead); localHead = localHead.next) {
- values.push(fn(localHead.data));
- }
- return linkedList(...values);
- };
- const withFilter = ({ start, linkedList }) => (fn) => {
- if (typeof fn != 'function') {
- throw Error('Predicate has to be a function.');
- }
- if (isEmpty(start)) {
- return null;
- }
- const values = [];
- for (let localHead = start; isInside(localHead); localHead = localHead.next) {
- if (fn(localHead.data)) {
- values.push(localHead.data);
- }
- }
- return linkedList(...values);
- };
- const withReduce = ({ start, linkedList }) => (...args) => {
- const [fn] = args;
- if (typeof fn != 'function') {
- throw Error('Reducer has to be a function.');
- }
- if (isEmpty(start)) {
- return null;
- }
- let [localHead, value] = args.length == 1
- ? [start.next, start.data]
- : [start, args[1]];
- for (/* above, for readability */; isInside(localHead); localHead = localHead.next) {
- value = fn(value, localHead.data)
- }
- return value;
- };
- const withInspect = ({ start, head } = {}) => () => {
- console.groupCollapsed('Full inspection');
- const iterateOver = (start, fn) => {
- for (let localHead = start, index = 1; isInside(localHead); localHead = localHead.next, index += 1) {
- fn(index, localHead);
- }
- }
- iterateOver(start, (index, localHead) => {
- console.log(`#${index}:`, localHead.data);
- })
- iterateOver(start, (index, localHead) => {
- console.log(`#${index}:`, localHead);
- });
- try {
- console.log(`Structure: ${JSON.stringify(start)}`);
- } catch(error) {
- console.info('Circular structure.');
- }
- console.log('start', start);
- console.log('head', head);
- if (start === null) {
- console.log('List is empty');
- } else {
- console.log('next', head.next);
- console.log('prev', head.prev);
- console.log('is head at start:', head == start);
- }
- console.groupEnd();
- };
- const withIterator = ({ start } = {}) => function* ({ finite = false } = {}) {
- for (let localHead = start; isInside(localHead); localHead = localHead.next) {
- yield localHead.data;
- if (finite && localHead == start) {
- break;
- }
- }
- };
- // api builder
- const enumerableValue = (value, enumerable) => ({ value, enumerable });
- const enumerable = (value) => enumerableValue(value, true);
- const nonEnumerable = (value) => enumerableValue(value, false);
- const api = ({ head, start, end, linkedList }) =>
- Object.defineProperties({}, {
- current: enumerable(isNotEmpty(start) && isInside(head)? head.data : null),
- isOutside: enumerable(isOutside(head)),
- isBefore: enumerable(isBefore(head)),
- isAfter: enumerable(isAfter(head)),
- isInside: enumerable(isInside(head)),
- isEmpty: enumerable(isEmpty(start)),
- prev: nonEnumerable(withPrev({ api, head })),
- next: nonEnumerable(withNext({ api, head })),
- // clone: withClone({ start, linkedList }),
- map: nonEnumerable(withMap({ start, linkedList })),
- filter: nonEnumerable(withFilter({ start, linkedList })),
- reduce: nonEnumerable(withReduce({ start, linkedList })),
- // reduceRight
- // concat
- // add + insert
- [Symbol.iterator]: nonEnumerable(withIterator({ start })),
- inspect: nonEnumerable(withInspect({ start, head })),
- });
- // export
- const linkedList = (...items) => {
- const { start, start: head, latest: end } = createList(...items);
- return api({ head, start, end, linkedList });
- }
- const LinkedList = {
- of: linkedList,
- };
- // demos
- function demo(name, fn) {
- console.groupCollapsed(name);
- fn();
- console.groupEnd();
- }
- function demo1() {
- // General demo
- console.log('LinkedList.of(1, 2, 3, 4, 5, 6)');
- const list1 = LinkedList.of(1, 2, 3, 4, 5, 6)
- console.log(list1);
- list1.inspect();
- console.groupEnd();
- }
- function demo2() {
- // Iterator demo
- console.info('const list2 = LinkedList.of(1987, 1988, 1989, 1990, 1991, 1992);')
- const list2 = LinkedList.of(1987, 1988, 1989, 1990, 1991, 1992);
- console.log(list2);
- console.info('[...list2]');
- console.log([...list2])
- }
- function demo3() {
- // Next prev
- console.info('const list2 = LinkedList.of(1999, 2000, 2001, 2002, 2003, 2004);')
- const list3 = LinkedList.of(1999, 2000, 2001, 2002, 2003, 2004);
- console.log(list3);
- console.log('list3.current', list3.current);
- console.log('list3.next().current', list3.next().current);
- console.log('list3.next().current', list3.next().current);
- console.log('list3.next().next().current', list3.next().next().current);
- console.log('list3.next().current == list3.next().current', list3.next().current == list3.next().current);
- console.log('list3.next() == list3.next()', list3.next() == list3.next());
- }
- function demo4() {
- // Empty list
- const list4 = LinkedList.of();
- console.info('const list4 = LinkedList.of();');
- console.log(list4);
- console.log('list4.isEmpty', list4.isEmpty);
- list4.inspect();
- }
- function demo5() {
- // Map / filter / reduce
- const list5 = LinkedList.of(1, 2, 3, 4, 5, 6);
- console.info('const list5 = LinkedList.of(1, 2, 3, 4, 5, 6);');
- console.log('Original', [...list5]);
- console.log('Mapped (doubled)', [...list5.map((value) => value * 2)]);
- console.log('Filtered (odd)', [...list5.filter((value) => value % 2)]);
- console.log('Reduced (summed)', list5.reduce((a, b) => a + b, 0));
- }
- // main
- console.clear();
- // demo('General demo', demo1);
- // demo('Iterator demo', demo2);
- // demo('Prev/next demo', demo3);
- // demo('Empty list', demo4);
- demo('Map / filter functions', demo5);
- console.info('The end.')
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement