Skip to content

Conversation

@rfearing
Copy link
Owner

@rfearing rfearing commented Oct 29, 2021

Work Done

  • Added a SinglyLinkedList class
  • Added a SinglyLinkedListNode class, to be utilized in SinglyLinkedList class
  • Added Jest tests for the newly created SinglyLinkedList class

Note, this is a bigger PR than originally intended, but each method is broken out into a commit.

What I'd like in a review:

  • Does the logic itself make sense and is it clear?
  • Would you do it a different way?
  • Is there anything you'd add or do differently?
  • Is there anything in the initial setup: Jest, NPM, Typescript etc, that you'd choose differently?

@rfearing rfearing marked this pull request as ready for review October 29, 2021 12:55
@rfearing rfearing requested a review from gtempus October 29, 2021 12:55
@rfearing rfearing force-pushed the add-singly-linked-list branch 2 times, most recently from a39683a to 4214c3a Compare October 29, 2021 17:25
@rfearing rfearing force-pushed the add-singly-linked-list branch from 4214c3a to 24568a7 Compare October 29, 2021 17:30
@rfearing rfearing force-pushed the add-singly-linked-list branch from 597dfaf to b1f12a7 Compare October 29, 2021 17:57
@rfearing rfearing changed the title Add singly linked list Add SinglyLinkedList Data Structure Class Oct 29, 2021
Copy link
Collaborator

@gtempus gtempus left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you so much for letting me be a part of your project, @rfearing! I hope I added a couple of worthwhile things to think about with regards to data structures and OOD.

- [ ] Hash Table
- [ ] Graph

I'm utilizing the [Wiki page as a "lessons learned" section][lessons-learned]. Hopefully it'll help future me, and whoever stumbles upon this repo.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice! I need to get into a better habit of doing this. It'll be cool to see what this gets filled with.

Comment on lines +5 to +7
> [A singly linked list] is the simplest type of linked list in which every node contains some data and a pointer to the next node of the same data type. The node contains a pointer to the next node means that the node stores the address of the next node in the sequence. A single linked list allows traversal of data only in one way

<img src="../../assets/singly-linked-list.png" width="300" alt="Graph representation of a singly linked list" />
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Including a definition and diagram is great! 🤓

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there any plan to compare/contrast with existing Javascript data structures? For example, were you going to do any research into how Array or TypedArray is implemented and compare that to what you have? Any interest in doing performance analysis with a profiler, etc?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With respect to implementation and constraints, it would be nice to give an example of where a dynamically allocated array is useful. Maybe what types of environments it's best in? Trade-offs with space vs time? Big O analysis?

I don't think you have to have that stuff. I'm just making sure I know what all your goals are for this project.

@@ -0,0 +1,217 @@
import { SinglyLinkedNode as Node } from './node';

export class SinglyLinkedList<T> {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a full-featured SLL! 💪 Are you attempting to match the interface of Array or just picking interesting functionality?

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've been going through this Udemy course and have been adopting / modifying the methods the instructor teaches. One thing tho, I think I may change the method names to be more descriptive. E.g. insertAtEnd instead of push OR at least make aliases for those methods. Even in working on the class I had to keep asking which is which hahah. Any thoughts on method names?

* @param {number} index - Where the node is located in the list.
* @returns {SinglyLinkedNode | null}
*/
get(index: number) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would recommend not returning a SinglyLinkedNode for all your getters. Assuming a client that is interested in storing objects in a data structure and not concerned with the how, your implementation actually breaks encapsulation. Clients are now aware of a SinglyLinkedNode and will use it with unintended consequences. I would just return the value that the client entrusted to you. This also has the beauty of preserving symmetry with the classes setters:
Screen Shot 2021-11-01 at 10 43 45 AM

this.head = newNode;
this.tail = this.head;
} else {
this.tail?.setNext(newNode);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not a fan of optional chaining when the implementation is completely controlled by the creator/implementer of the class and not its clients. This alone is a good exercise for software design. Make sure you clearly state the invariants of each interaction (method, function, etc). Is it okay that this class isn't sure that tail is non-null when it also claims to be not empty? hmmm. A good way to track invariants is to use assert in your methods where you expect those invariants to hold for every single occasion. There is a good discussion of this in Fowler's Refactoring 2nd Ed. if you're curious.

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@gtempus I just read the Introduce Assertion portion you mentioned. This is definitely going into a "Lessons Learned" section.

I was thinking "man, I don't love that I have to do optional chaining here, but TS keeps complaining".

Even with an assert I'm getting the same TS error. Maybe I need to change my tsconfig or maybe there's something else you can suggest? Would love to pair too on maybe removing one of these optional chains?

image

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I actually just realized... I couuuuld optionally chain in the assertion. Thoughts?
image

Comment on lines 5 to 14
test('SinglyLinkedList push method correctly adds to the end of the list', () => {
expect(newList.head).toBeNull();
newList.push('1');
newList.push('2');
newList.push({ hello: 'world' });
expect(newList.head?.getValue()).toEqual('1');
expect(newList.head?.getNext()?.getValue()).toEqual('2');
expect(newList.tail?.getValue()).toEqual({ hello: 'world' });
expect(newList.length).toEqual(3);
});
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I love all the tests but I would encourage you to break them up. Too many expects lead to tests breaking all the time with the smallest of changes.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, I'm not sure it's a good idea to allow access to head and tail. Again, it breaks encapsulation and opens your nice SLL to all sorts of abuses. Testing the implememtation and not the interface will also make your tests more brittle.

I haven't tried these javascript features yet: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes/Private_class_fields

@rfearing rfearing force-pushed the add-singly-linked-list branch from 37ebceb to 736625a Compare November 27, 2021 22:09
@rfearing
Copy link
Owner Author

@gtempus Thanks for this feedback. I made some of the changes we discussed. I'll probably do a comparison between the structures closer to the end. Care to give it another peek?

}

next: SinglyLinkedNode<T> | null;
value: T | null;
Copy link
Collaborator

@townofdon townofdon Nov 29, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since you're using Typescript, you can add private access modifier to prevent accidentally writing directly to next or value when using a LinkedList node

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Love this! Thank you!


describe('.getLength', () => {
it('return the length of the list', () => {
expect(newList.getLength()).toEqual(2);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might suggest a beforeEach method as a cleanup to isolate all the tests from each other, making them more deterministic. (This test case depends on a previous test case)

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good call. Made that change :-D

@townofdon
Copy link
Collaborator

Looks good! I agree with @gtempus 's comments above (we both made a similar observation about adding private access modifiers haha)

Additionally, as a personal preference, I like to include usage docs whenever possible, but since this is a learning exercise, having the tests be the place to show off the functionality may suffice.

Also, I noticed that you're using JSDocs, which are great - Typescript does allow for some auto doc functionality however by including the return type of a function.

const myFunc = (): ReturnType => { ... }

In VSCode at least, hovering over myFunc anywhere in the codebase would should that it has a return value of ReturnType.

@rfearing rfearing merged commit 11ac939 into main Dec 9, 2021
@rfearing rfearing deleted the add-singly-linked-list branch December 9, 2021 20:50
rfearing added a commit that referenced this pull request Dec 9, 2021
* Add SinglyLinkedList with push method

* Add Pop to SinglyLinkedList

* Add lessons learned to README

* Add todo methods and snub tests

* Add shift method to SinglyLinkedList

* Add unshift method to SinglyLinkedList class

* Add get method to SinglyLinkedList

* Add link to SinglyLinkedList README

* Add set method to SinglyLinkedList

* Add insert and remove methods to SinglyLinkedList

* Add reverse method to SLL

* Make methods private and remove optional chaining

* Update method names

* Update tests

* Add list v array to README

* Update class to use TS private modifier

* Update tests to utilize beforeEach
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants