-
Notifications
You must be signed in to change notification settings - Fork 0
Add SinglyLinkedList Data Structure Class #1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
a39683a to
4214c3a
Compare
4214c3a to
24568a7
Compare
597dfaf to
b1f12a7
Compare
gtempus
left a comment
There was a problem hiding this 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. |
There was a problem hiding this comment.
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.
| > [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" /> |
There was a problem hiding this comment.
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! 🤓
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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> { | |||
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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?
src/singlyLinkedList/index.ts
Outdated
| * @param {number} index - Where the node is located in the list. | ||
| * @returns {SinglyLinkedNode | null} | ||
| */ | ||
| get(index: number) { |
There was a problem hiding this comment.
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:

src/singlyLinkedList/index.ts
Outdated
| this.head = newNode; | ||
| this.tail = this.head; | ||
| } else { | ||
| this.tail?.setNext(newNode); |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
tests/singlyLinkedList.test.ts
Outdated
| 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); | ||
| }); |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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
37ebceb to
736625a
Compare
|
@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? |
src/singlyLinkedList/node.ts
Outdated
| } | ||
|
|
||
| next: SinglyLinkedNode<T> | null; | ||
| value: T | null; |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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); |
There was a problem hiding this comment.
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)
There was a problem hiding this comment.
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
|
Looks good! I agree with @gtempus 's comments above (we both made a similar observation about adding 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. In VSCode at least, hovering over |
* 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


Work Done
SinglyLinkedListclassSinglyLinkedListNodeclass, to be utilized inSinglyLinkedListclassSinglyLinkedListclassNote, this is a bigger PR than originally intended, but each method is broken out into a commit.
What I'd like in a review: