How to pass a linked-list? - javascript

I was given this as a solution to delete all duplicate numbers in a Linkded list. But I don't understand how to pass in the linked list?
var deleteDuplicates = function(head) {
// sets current node to be head of list
let current = head
// runs until we are at the end of the list
while (current !== null && current.next !== null) {
// checks to see if the current value and the next value are the same
if (current.val === current.next.val){
// skips over the duplicate and the next value becomes 2x next
current.next = current.next.next
// current value and the next value are not the same
} else {
// moves to the next node on the list to run through the while again
current = current.next
}
}
// returns the linked list with no duplicates
return head
};

A example to pass a List to function getListSize(). Is this what you want?
class ListNode {
constructor(data) {
this.data = data
this.next = null
}
}
class LinkedList {
constructor(head = null) {
this.head = head
}
}
let node1 = new ListNode(2)
let node2 = new ListNode(5)
node1.next = node2
let list = new LinkedList(node1)
function getListSize(list){
let count = 0;
let node = list.head;
while (node) {
count++;
node = node.next
}
return count;
}
console.log(getListSize(list));

You created a class but don't instantiate it, to do so call the constructor of the class using the new keyword:
const instance = new LinkedList();
then you need to pass the head property of that instance to the function, currently you are sending an undefined value (head doesn't exist outside the deleteDuplicates function's scope).
deleteDuplicates(instance.head);
i also noticed that your function deleteDuplicates won't work for what you want to do, here is a function that should do what you are trying to achieve:
function deleteDuplicates(list) {
if (!Array.isArray(list)) {
console.error(`Given argument is not an Array: ${list}`);
return;
};
return list.map((element, index, array)=>{
if (array.indexOf(element)!==index) return undefined;
return element;
})
.filter((element)=>(element!==undefined));
}
for more informations about what i said:
how classes work: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/class
understand scope: https://developer.mozilla.org/en-US/docs/Glossary/Scope

Related

Why is my linked list object in JavaScript not updating when I try to add a node to the beginning of the list?

I'm trying to create a linked list using a factory function. When I run the method append on the list console.log displays the correct value. The prepend method on the other hand doesn't seem to be working when I console.log the resulting list. However, when I run a method to return the value of the list and log that I get the value I'm looking for. The code is as follows:
//linked list factory function
const LinkedListFactory = (initialNode) => {
//declare initial value of the list
let head = { value: initialNode.value, nextNode: initialNode.nextNode };
//method to return list
const giveList = () => {
return head;
};
//method to return last node in list
const getLast = () => {
let lastNode = head;
if (lastNode) {
while (lastNode.nextNode) {
lastNode = lastNode.nextNode;
}
}
return lastNode;
};
//method to append node to the end of the list
const append = (node) => {
getLast().nextNode = node;
};
//method to add node to the front of the list (not working)
const prepend = (node) => {
let temp = { ...head };
node.nextNode = temp;
head = node;
//gives the correct updated value of the list
console.log(head);
};
return { head, append, prepend, giveList };
};
//node factory function to create data nodes
const NodeFactory = (value = null, nextNode = null) => {
return { value, nextNode };
};
//creating test nodes
let node1 = NodeFactory("This is node 1");
let node2 = NodeFactory("This is node 2");
let node3 = NodeFactory("This is node 3");
let node4 = NodeFactory("This is node 4");
let list = LinkedListFactory(node1);
console.log(list.head);
list.append(node2);
console.log(list.head);
list.prepend(node3);
//These two statements give different values in the console
console.log(list.head);
console.log(list.giveList());
I did some googling to try and pin down the problem but I've had no luck so far. I expected node3 to be added to the beginning of the linked list and point to node1 which would then point to node2. When I console.log the list it only shows node1 and node2 but not node3. When I run a method to return the linked list and log it I get the value I'm looking for. I don't know why it's doing this behaviour and I'm very confused.
The problem is that head = node is not actually doing what you think it is doing.
First let's have a look at the LinkedListFactory, which internally declares a variable head and initializes it with the contents of the Node that is passed to LinkedListFactory. After declaring the methods you return a new object with them and the head node in it. (I will call it the "list" from now)
JavaScript objects are always references to their underlying values. This means after LinkedListFactory returns, the "list" object's head property will point to the same value as the head variable that was declared in the LinkedListFactory.
When you, in prepend, set the head variable equal to a new temp Node, the "list" object's head property will still point to the original value.
To achieve what you want you need to change prepend to look like something like this:
function prepend(node) {
let temp = this.head; // temp points to the old head
this.head = node; // overwrite pointer to head on list
this.head.nextNode = temp; // point to old head
head = this.head; // keep head and this.head in sync
}
In this case this will refer to the object the prepend function was called on, so the "list", and we can modify it.
In your implementation we are only changing what the internal variable head is pointing to. In this implementation we change the head property of the "list".
I can recommend you to have a look at these MDN pages:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Working_with_Objects
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this
I thought about this a little bit more. Your implementation works, you just give a meaning to the "list"'s head property where there is none. You can simply remove it as it is only confusing:
//linked list factory function
const LinkedListFactory = (initialNode) => {
//declare initial value of the list
let head = { value: initialNode.value, nextNode: initialNode.nextNode };
//method to return list
const giveList = () => {
return head;
};
//method to return last node in list
const getLast = () => {
let lastNode = head;
if (lastNode) {
while (lastNode.nextNode) {
lastNode = lastNode.nextNode;
}
}
return lastNode;
};
//method to append node to the end of the list
const append = (node) => {
getLast().nextNode = node;
};
//method to add node to the front of the list (not working)
const prepend = (node) => {
let temp = { ...head };
node.nextNode = temp;
head = node;
//gives the correct updated value of the list
console.log(head);
};
return { append, prepend, get head() { return giveList(); } }; // I aliased giveList to a `head` getter
};
//node factory function to create data nodes
const NodeFactory = (value = null, nextNode = null) => {
return { value, nextNode };
};
//creating test nodes
let node1 = NodeFactory("This is node 1");
let node2 = NodeFactory("This is node 2");
let node3 = NodeFactory("This is node 3");
let node4 = NodeFactory("This is node 4");
let list = LinkedListFactory(node1);
console.log(list.head);
list.append(node2);
console.log(list.head);
list.prepend(node3);
console.log(list.head);

I am trying to figure out how to delete and entry by value from a singly linked-list

I am really having trouble thinking. I am trying to code but mostly copy and paste as I am trying to learn. Hear is the code I've put together so far.
function LinkedList() {
this.head = null;
this.length++;
}
function Node(val) {
this.val = val;
this.next = null;
}
LinkedList.prototype.add = function(val) {
let newNode = new Node(val);
newNode.next = this.head;
this.head = newNode;
this.length++;
return this.head;
};
LinkedList.prototype.delete = function(val) {
let current = this.head;
while (current.value !== val) {
current = current.next;
}
if (current.next) {
current.value = current.next.val;
current.next = current.next.next;
}
};
let linkedList = new LinkedList();
linkedList.add(1);
linkedList.add(2);
console.log(linkedList.delete(1))
When I console log the last line it says "Cannot read properties of undefined (reading 'value')
P.S. I don't even know how people "low g" is that even possible?
Thank you
Several issues:
The loop condition current.value !== val will be true for all nodes because current.value is undefined. It should be current.val.
Since the loop condition is always true, eventually current will be null, and then current.value will trigger the error you got. You should actually expect this to happen also when the above error is fixed, because delete may be called with a value that is not present in the list, and then also the loop will run until current is null. You should exit before this happens.
The logic you have used to delete a node consists of copying the next node's value, and removing the next node from the list. But this will not work when the node to delete is the very last one. In that case your code will run into an error. Moreover, this is a structural problem: there is no next node to delete, so you'll end up with the same number of nodes. You should really stop the search one node earlier and then delete the next node.
this.length++ in the LinkedList constructor makes no sense, since the this.length property does not exist yet when ++ is executed. Moreover, there is no node yet in this list, so the length should just be initialised to 0.
The delete method should adjust this.length when the deletion is successful.
Doing a console.log of what delete returns is useless: delete is not returning anything, so you are just going to see undefined in the console. It is not clear why and what you wanted to print. You could let the delete method return a boolean, so to indicate whether a node was deleted or not (in case it was not found). If the idea was to print the list after the deletion, then you need to do that separately, and first need to provide a method that allows printing a linked list in a nice way, otherwise you'll just get an object representation as output.
The add method should not return the head node. This is not very useful, as the caller should not be bothered with Node instances. If you want to return anything, then return the linked list itself (this). That way you can allow someone to chain add methods, like linkedlist.add(1).add(2).
Not a problem, but the syntax you have used for creating a constructor and prototype functions is old-fashioned. Since ECMAScript2015 we can make use of the so much nicer class syntax. So I suggest you use it.
Here is a new version of your code taking all of the above into account:
class Node { // Use class syntax
constructor(val) {
this.val = val;
this.next = null;
}
}
class LinkedList {
constructor() {
this.head = null;
this.length = 0; // There are no nodes yet
}
add(val) {
let newNode = new Node(val);
newNode.next = this.head;
this.head = newNode;
this.length++;
return this; // Don't return the head
};
delete(val) {
let current = this.head;
// current could be null, so protect against run time errors
if (!current) return false; // Indicate that no deletion occurred
if (current.val === val) {
this.head = current.next;
this.length--; // adjust length
return true; // Indicate success!
}
// Your algorithm can never delete the last node, so we use
// a different algorithm, which looks one node ahead
while (current.next) {
if (current.next.val == val) { // Found it
// Delete it
current.next = current.next.next;
this.length--; // adjust length
return true; // success
}
current = current.next;
}
}
// Make the linked list iterable. This helps with printing
*[Symbol.iterator]() { // Special property name used by iteration
for (let node = this.head; node; node = node.next) {
yield node.val;
}
}
}
let linkedList = new LinkedList();
linkedList.add(1).add(2).add(3).add(4);
// Print the list by triggering an iteration
console.log(...linkedList); // 4 3 2 1
if (linkedList.delete(2)) console.log("successfully deleted 2");
console.log(...linkedList); // 4 3 1
console.log("this list has", linkedList.length, "items");

Trying to add a linkedlist to an empty linked list javacript

I am trying to add another linkedlist to an empty linked list. l1 is an empty linked list and l2 has 3 elements.
///Code snippet
current = l1.head;
if (current === null){
current = l2.head;
}
When i run this and print l1 I get undefined.
However when I change the code to :
current = l1.head;
if (current === null){
l1.head = l2.head;
}
it prints the elements of l2 along with an undefined in the end. What is the reason for this?
Adding the code snippet for the class of the linked list i used:
// Creating a class for node that will be the building block for our linked list.
// Each element in the linked list will be stored in a node.
class Node{
constructor(data = null, next = null){ // The node will have two attributes: the data it has and the pointer to next node
this.data = data;
this.next = next;
}
}
// Creating the linked list class
class linkedlist {
constructor(){ // The main attributes of the linked list are the head and its size.
this.head = null;
this.size = 0;
}
// Method to insert data at the start of the linked list
insert(data){
this.head = new Node(data, this.head); // Assigning the new data to the head of the linked list. And whatever is in the head is assigned to the next parameter.
this.size++; // Increase the size to keep track.
}
// Method to insert data at end
insertEnd(data){
let current = this.head; // Use current to traverse through the linked list. setting it to head.
if(current === null){
this.head = new Node(data);
}
else{
while(current.next !== null){ // Using a while loop to traverse through the linked list.
current = current.next; // Going all the way to the end. when the next becomes null we will exit the while loop indicating we reached the end.
}
current.next = new Node(data); // Now that we reached the end we assign the next to the new node.
}
this.size++; // Increase size to keep track.
}
// Method to insert data at a particular index
insertAt(index, data){
if(this.size < index){ // Cheking if the index requested is in bounds
return console.log('Out of bounds');
}
else{
var current = this.head; // Using current to traverse the linked list and initializing it to the head.
var previous; // Initializing another variable previous.
let i = 0;
while(i < index){ // Using a while loop to traverse to the index.
previous = current; // Assigning current to previous so that we can have a record of previous and next element in the linked list.
i++;
current = current.next; // Traversing the linked list.
}
// While loop will exit once we reach the node just before the index where we want insert the data.
const node = new Node(data); // Creating a new node with the new data.
node.next = current; // So the current node will be assigned as the next since it is at the index we want to insert the data.
previous.next = node; // The previous which has the data of the node before the index will get the new node as its next.
this.size++; // Increase size to keep track.
}
}
// Method to remove element from an index
removeat(index){
let count = 0, previous; // Initializing some variables.
let current = this.head; // setting current to head to be able to traverse the linked list.
if (index > this.size){ // Checking if the index is out of bounds
console.log("Index out of bounds");
}
else{
while(count < index){ // Using while loop to traverse through the linked list till the index
previous = current; // Assigning current node to previous to keep track of consecutive nodes.
current = current.next;
count++;
}
// While loop exits when we reach the index where we want to remove the data from.
previous.next = current.next; // To remove the node at the index assign the next of the previous node to the next of the current node. Thus removing the pointer to the current node.
}
this.size--; // Decrementing to keep track of size.
}
// Method to clear all the elements from the linked list
clear(){
this.head = null; // Assigning the head to null automatically clears all the data in the linked list cos it will have no reference to any elements in the list anymore.
this.size = 0; // Updating the size of the linked list.
}
// Method to display all the data in the linked list
show(){
let current = this.head; // Initializing current to head to traverse through the linked list.
while(current != null){ // While current not null.
console.log(current.data); // Printing the data to the console.
current = current.next;
}
}
}
let l1 = new linkedlist();
let l2 = new linkedlist();
l2.insertEnd(1);
l2.insertEnd(3);
l2.insertEnd(4);
Simple function to merge the two linked lists.
var mergeTwoLists = function(l1, l2) {
current = l1.head;
if (current === null){
current = l2.head;
}
return l1;
};
What I am expecting is the l1 list now points to the l2 list's head. But in turn I am still getting null when I print out l1.
The first code snippet doesn't work, because a list can only get additional nodes when you either set its head reference, or when you set one of its node's next references. Your first code doesn't do any of that, it merely sets a local variable current, which does not affect a list.
The fact that current was previously set to l1.head doesn't influence what another assignment to current does. It is a separate variable; it is not the head property of the list.
The second code snippet works fine, as can be seen below. I also completed your merge function, as it could apply the same principle as you already have for insertEnd. I removed all the other methods that are not needed for your question:
class Node{
constructor(data = null, next = null) {
this.data = data;
this.next = next;
}
}
class linkedlist {
constructor() {
this.head = null;
this.size = 0;
}
insertEnd(data) {
let current = this.head;
if (current === null) {
this.head = new Node(data);
} else {
while (current.next !== null) {
current = current.next;
}
current.next = new Node(data);
}
this.size++;
}
show() {
let current = this.head;
while (current != null) {
console.log(current.data);
current = current.next;
}
}
}
function mergeTwoLists(l1, l2) {
current = l1.head;
if (current === null){
l1.head = l2.head;
} else { // Same principle as in insertEnd:
while (current.next !== null) {
current = current.next;
}
current.next = l2.head;
}
l1.size += l2.size // Don't forget!
return l1;
};
let l1 = new linkedlist();
let l2 = new linkedlist();
l2.insertEnd(1);
l2.insertEnd(3);
l2.insertEnd(4);
console.log("list 1:");
l1.show();
console.log("------");
console.log("list 2:");
l2.show();
console.log("------");
mergeTwoLists(l1, l2);
console.log("merge of list 1 and list 2:");
l1.show();
console.log("------");
// Another test
let l3 = new linkedlist();
l3.insertEnd(13);
console.log("list 3:");
l3.show();
console.log("------");
mergeTwoLists(l1, 42);
console.log("merge of list 1 and list 3:");
l1.show();

Implement sorting in linked list in JavaScript (bubble sort or another)

I am trying to implement Bubble sort for a linked list in JavaScript. I was looking for similar questions but only found implementation in C ++ or Java. I would be grateful for your help.
It would be great to do BubbleSort but if there are other sorting options, I will also be happy to see their implementation. I tried different options to implement sorting in the linked list but they didn't work.
Now I just have methods that add elements in begin or end of the list.
Below is the implementation of the list.
LinkedListNode
export class LinkedListNode {
public value;
public prev;
public next;
constructor(value) {
this.value = value;
this.next = null;
this.prev = null;
}
}
LinkedList
import { LinkedListNode } from './LinkedListNode';
export class LinkedList {
private head;
private tail;
addHeadNode = (value) => {
const newLinkendListNode = new LinkedListNode(value);
if (!this.head) {
this.head = newLinkendListNode;
this.tail = newLinkendListNode;
} else {
this.head.prev = newLinkendListNode;
newLinkendListNode.next = this.head;
this.head = newLinkendListNode;
}
};
addTailNode = (value) => {
const newLinkendListNode = new LinkedListNode(value);
if (!this.tail) {
this.head = newLinkendListNode;
this.tail = newLinkendListNode;
} else {
this.tail.next = newLinkendListNode;
newLinkendListNode.prev = this.tail;
this.tail = newLinkendListNode;
}
};
getByIndex = index => {
let currentNode = this.head;
let count = 0;
while (currentNode) {
if (count === index) {
console.log(currentNode);
return currentNode;
}
count++;
currentNode = currentNode.next;
}
return -1;
};
}
If I had to do a bubble sort for a LinkedList, I would start with what I do know or what I could find.
Let's assume you knew how to do a bubble sort for numbers and that you decided to go with TypeScript because it was easier to refactor later on into a LinkedList.
I would then have done a class-based implementation like so:
class Sorter {
constructor(public collection: number[]) {}
sort(): void {
}
}
So I defined and initialized a collection of an array of numbers inside a class of Sorter with a method of sort() that will do the actual sorting like so:
class Sorter {
constructor(public collection: number[]) {}
sort(): void {
const { length } = this.collection;
}
}
I destructured out the length from the collection, which is an array of numbers. Then, one thing that is good to remember, a bubble sort requires one for loop nested inside another like so:
class Sorter {
constructor(public collection: number[]) {}
sort(): void {
const { length } = this.collection;
for (let i =0; i < length; i++) {
for (let j = 0; j < length - i - 1; j++) {
}
}
}
}
So remembering the nested double for loop is the easy part, now inside we have to remember that in bubble sort we are comparing two elements, let's say the the number 10 and number 3 and we are asking, is 10 > than 3? If so, swap them, but we are just wanting to write the comparison logic for now and it's done like so:
class Sorter {
constructor(public collection: number[]) {}
sort(): void {
const { length } = this.collection;
for (let i =0; i < length; i++) {
for (let j = 0; j < length - i - 1; j++) {
if (this.collection[i] > this.collection[i + 1]) {
}
}
}
}
}
So if the left hand element is greater then swap them and we designate that like so:
class Sorter {
constructor(public collection: number[]) {}
sort(): void {
const { length } = this.collection;
for (let i =0; i < length; i++) {
for (let j = 0; j < length - i - 1; j++) {
if (this.collection[i] > this.collection[i + 1]) {
const leftHand = this.collection[i];
this.collection[j] = this.collection[j + 1];
}
}
}
}
}
So I assigned the leftHand side, then I took the right hand side and threw it over to the left with this line: this.collection[j] = this.collection[j + 1];
Then I need to take the left hand side and throw it over to the right like so:
class Sorter {
constructor(public collection: number[]) {}
sort(): void {
const { length } = this.collection;
for (let i =0; i < length; i++) {
for (let j = 0; j < length - i - 1; j++) {
if (this.collection[i] > this.collection[i + 1]) {
const leftHand = this.collection[i];
this.collection[j] = this.collection[j + 1];
}
}
}
}
}
Okay, so this is pretty good. So now on to how to do a LinkedList version of this.
I said to do this in TypeScript because now I can avail myself of something called an interface.
I cannot sing the praises of interfaces enough. Interfaces are excellent not because we can describe a type, but because we can set up a contract between one class and another class and say if you do xyz, imagine all the functionality I am going to give you.
That's why we care about interfaces in TypeScript.
So how can an interface help me with a Linked List implementation?
Well, we can break down what a bubble sort is doing, the previous answer already mentioned something about swapping, so that's one. We can create a method for that:
interface Sortable {
swap(leftIndex: number, rightIndex: number): void;
}
So above I am saying the interface now has a method of swap() with arguments of leftIndex and rightIndex, both are numbers and they return nothing.
What else do we do in bubble sort?, before we swap we compare, we ask is the element on the left greater than the element on the right? By doing that comparison we also check for the length of each element. So we check for length and we compare, so we can add those to the interface like so:
interface Sortable {
length: number;
compare(leftIndex: number, rightIndex: number): boolean;
swap(leftIndex: number, rightIndex: number): void;
}
Now inside of that Sorter class we can replace the public collection from having to be a number of arrays to being the interface of Sortable:
class Sorter {
constructor(public collection: Sortable) {}
sort(): void {
const { length } = this.collection;
for (let i =0; i < length; i++) {
for (let j = 0; j < length - i - 1; j++) {
if (this.collection[i] > this.collection[i + 1]) {
const leftHand = this.collection[i];
this.collection[j] = this.collection[j + 1];
}
}
}
}
}
So what we are well on our way of doing here is taking this basic bubble sorting algorithm that we know works with an array of numbers and applying an interface so it can then work with other data structures as well, including LinkedLists.
We separated out swapping and comparing. So now rather than working with this.collection as if it were a plain array, I delegated this out to the interface and now I can say this.collection.compare() and pass in the two indices I want to compare which is going to be j and j + 1` and I can do the same thing for the swapping method like so:
class Sorter {
constructor(public collection: Sortable) {}
sort(): void {
const { length } = this.collection;
for (let i =0; i < length; i++) {
for (let j = 0; j < length - i - 1; j++) {
if (this.collection.compare(, j + 1)) {
this.collection.swap(j, j + 1);
}
}
}
}
}
So in a Linked List data structure we have a series of different nodes, every node contains one value. A value can be of any arbitrary type, but let us build our LinkedList to store numbers.
A node is going to have a single number, it's also going to have a reference to the next node inside of a chain. When we get all the way down to the last node right here, the last one is going to reference a value of null and that means we have hit the end of our chain.
So I am going to have two classes in total, a Node class that represents each individual node and a LinkedList class, the LinkedList class will have a reference to the head Node and it's also going to have a bunch of methods associated with it to manipulate the overall list.
So the LinkedList class will have a method called length to return the total number of nodes. It will have an add method to add in a new node, an at method to return the element at a very specific index in the chain, compare, swap and a print method to iterate through the whole list and print out every value inside of it.
The goal of the print() method is to verify that I have corrected sorted the list at some point in time.
Inside of here I will first put together my implementation of a Node.
So at the top I added a new class called Node like so:
class Node {
next: Node | null = null;
constructor(public data: number) {}
}
A node has two properties, a value that’s a number, the actual number I am trying to store in a Node and then next which is going to be a reference to the next node in the chain or it might possibly be null and if it is, that means I have hit the end of the chain.
So my node will have two properties and you see I defined the constructor and used the shortened syntax of public data and that’s the number I am trying to track here. A node has a next property with a stand-alone notation because I don’t want to define the next node in the chain when I create a node, instead I want to be able to create a node first and then associate it with some other node in the chain later on, that’s why I am not sticking next inside the constructor.
Next is going to be of type node to say I am referencing another node or it will be of type null.
By default when I first create this thing it’s going to be null. When I create a node by itself it's not going to be attached or referencing any other node, so by default, next will be node.
That's the total implementation of a node.
Now I can start to put together the linked list.
Inside of the linked list I will have a reference to the head node, so head is going to be either a reference to a node or it might be null when I first create the linked list. If it's null that means the linked list is totally empty like so:
export class LinkedList {
head: Node | null = null;
So I said this thing has a head property that references a node so it's of type node or is null and I defaulted it to null, so the LinkedList starts out as totally empty.
That is the only property that a LinkedList is going to have.
From here on, it's just about adding all the methods. The first method will be the add method that takes in a number, it creates a node out of it and add that node at the end of the chain. So if I call add with a value or number of 2 I will essentially find the last node and stick on to the end a new node with a value of 2 and that new node would reference null.
So my add method will take in some number that I will refer to as data, I don’t expect for it to return anything so I annotated it with a return type of void.
add(data: number): void {
}
So right away I create a new Node() and pass in that number data like so:
add(data: number): void {
const node = new Node(data);
}
So then inside of here I need to consider two different cases, I need to ensure I actually have a head, if I don’t have a head yet and my list is entirely empty then I can take the new node created and assign it to the head property by saying if there is not a head then this.head will be the new Node() just created and return immediately like so:
add(data: number): void {
const node = new Node(data);
if (!this.head) {
this.head = node;
return;
}
Notice how I have a return type of void? But I put in a return statement anyway. When I have a return type of void, I can still have return statements, void just means I am not going to specifically return any useful values.
So I can still return early if I want to, I just can’t return an actual thing.
After that, I will be in the case where I already have a head in the node and I need to find the last node in the chain and add the newly created node to that.
To do so, I have to write out a while loop here that I will be using several times in this class to iterate through my chain like so:
export class LinkedList {
head: Node | null = null;
add(data: number): void {
const node = new Node(data);
if (!this.head) {
this.head = node;
return;
}
let tail = this.head;
while (tail.next) {
tail = tail.next;
}
So I am saying while tail.next, so while the head node has a next property, then advance that tail variable with tail is tail.next.
This statement is challenging to get your head around the first time.
So I am going to create a variable called tail and by default tail is going to reflect or point at the head node. I am then going to say, look at tail, if tail has a defined next property, so in other words if next is referring to an actual node then update my tail reference and say that tail is now referring to the next node. I will then look at the next node and as long as it has the defined next property, then update the tail reference again and go to the next one, I will repeat that process until I hit the very last node in the chain.
When I get there I say if tail.next refers to something, in that case it refers to null so that when I exit the while loop.
So when hitting the very last node, it will be captured here, while tail has a defined next property, when tail is eventually the very last node, next will be undefined and so the while loop will not continue after that.
Once I find that very last node, I am going to take node I just created and add it as the next property to the tail like so:
export class LinkedList {
head: Node | null = null;
add(data: number): void {
const node = new Node(data);
if (!this.head) {
this.head = node;
return;
}
let tail = this.head;
while (tail.next) {
tail = tail.next;
}
tail.next = node;
}
So once I get to this last node, take the node that was just created and have the next property of the last node refer to the node I just created.
So that is the first method of add().
Next one is length. The length list has to have a length property, if it's going to satisfy the interface like so:
export class LinkedList {
head: Node | null = null;
add(data: number): void {
const node = new Node(data);
if (!this.head) {
this.head = node;
return;
}
let tail = this.head;
while (tail.next) {
tail = tail.next;
}
tail.next = node;
}
get length(): number {
if (!this.head) {
return 0;
}
let length = 1;
let node = this.head;
while (node.next) {
length++;
node = node.next;
}
return length;
}
So that length returns a number and the length as you can see has a lot going on.
Inside of there I checked to see if there is not a head, if I don’t have a head at all, then I return zero because the list is empty, if I then get past that, I set up a while loop and iterate through the list. I created a counting variable called length and initialized it as one.
I got a reference to the first node in the change with let node = this.head;
So once again I am iterating through the entire chain and once I get to the very last node, next will be undefined and so the while loop will break out, at that point, length will reflect the total number of nodes that I crawled through.
And after the while loop I return length.
export class LinkedList {
head: Node | null = null;
add(data: number): void {
const node = new Node(data);
if (!this.head) {
this.head = node;
return;
}
let tail = this.head;
while (tail.next) {
tail = tail.next;
}
tail.next = node;
}
get length(): number {
if (!this.head) {
return 0;
}
let length = 1;
let node = this.head;
while (node.next) {
length++;
node = node.next;
}
return length;
}
at(index: number): Node {
if (!this.head) {
throw new Error("Index out of bounds");
}
let counter = 0;
let node: Node | null = this.head;
while (node) {
if (counter === index) {
return node;
}
counter++;
node = node.next;
}
throw new Error("Index out of bounds");
}
So once again, a quick check, if I don’t have a head property, if my LinkedList is empty and I am trying to get some element inside of it, I will throw an error of index out of bounds.
I also set up a while loop that says let counter at zero for that first node, I then create a node variable to refer to this.head. So while I have a node, if counter equals index, if I have counted all the way up to the appropriate index that means I found the node I am looking for, so in that case return the node, otherwise, then I need to continue iterating.
Counter plus plus to move on to the next node and I will update the node reference to the next node on the chain.
So if I get out of this while loop eventually without ever hitting the return statement I throw an error again.
Essentially, I went through the entire list of nodes that were found and I never found an index equal to the one I was looking for, which means someone was probably asking for an index that is just too great or larger than the actual Linked List.
Notice I added on a manual type annotation where I said node can either be Node or null:
let node: Node | null = this.head;
That was exhausting.
So now compare and swap from the interface.
compare(leftIndex: number, rightIndex: number): boolean {
if (!this.head) {
throw new Error("List is empty");
}
return this.at(leftIndex).data > this.at(rightIndex).data;
}
So this is going to take leftIndex and rightIndex that’s a number and return a boolean, this is where I immediately use the at method I put together.
If I don’t have anything in the list calling compare does not make any sense, so I will just throw a new error that says list is empty.
The errors are just for covering corner cases that we might run into if we try to test out weird inputs to the code.
Then I can return this.at(leftIndex) that will give me the node at leftIndex, I want to reference its data property to find the number in the actual node and then compare it to see if it's greater than the node value at the right index.
So now swap and print.
swap(leftIndex: number, rightIndex: number): void {
const leftNode = this.at(leftIndex);
const rightNode = this.at(rightIndex);
const leftHand = leftNode.data;
leftNode.data = rightNode.data;
rightNode.data = leftHand;
}
Swapping two elements inside a LinkedList can be extremely complicated, so I am going to cheat here and not swap the nodes, because if I swap the nodes I have to repair all the different references that might be broken.
Rather than swapping the nodes, I am going to swap the values, which is easier.
So I got a reference to the node on the left with leftNode is this.at(leftIndex) and then just as before write out some basic swapping logic.
Now print():
print(): void {
if (!this.head) {
return;
}
let node: Node | null = this.head;
while (node) {
console.log(node.data);
node = node.next;
}
}
So if we don’t have a head and this thing is empty, return right away, otherwise I iterate through the list with a while loop, while I have a node, do a console log of node.data and update the node variable to the next node on the chain and once again node.next could either be a Node or null.
Bubble sort can be implemented by adding this method:
bubbleSort() {
let last = this.tail;
while (last) {
let node = this.head;
while (node != last) {
let next = node.next
if (node.value > next.value) { // swap
[node.value, next.value] = [next.value, node.value];
}
node = next;
}
last = last.prev; // shorten the range that must be bubbled through
}
}
Here it is combined with your code (but using plain JavaScript, and allowing the constructor to take an argument):
class LinkedListNode {
constructor(value) {
this.value = value;
this.next = null;
this.prev = null;
}
}
class LinkedList {
constructor(iterable) { // Allow to initialise from an iterable
this.head = null;
this.tail = null;
if (iterable) {
for (let value of iterable) this.addTailNode(value);
}
}
addHeadNode(value) {
const newLinkendListNode = new LinkedListNode(value);
if (!this.head) {
this.head = newLinkendListNode;
this.tail = newLinkendListNode;
} else {
this.head.prev = newLinkendListNode;
newLinkendListNode.next = this.head;
this.head = newLinkendListNode;
}
}
addTailNode(value) {
const newLinkendListNode = new LinkedListNode(value);
if (!this.tail) {
this.head = newLinkendListNode;
this.tail = newLinkendListNode;
} else {
this.tail.next = newLinkendListNode;
newLinkendListNode.prev = this.tail;
this.tail = newLinkendListNode;
}
}
getByIndex(index) {
let currentNode = this.head;
while (currentNode) {
if (!index--) return currentNode;
currentNode = currentNode.next;
}
return null;
}
bubbleSort() {
let last = this.tail;
while (last) {
let node = this.head;
while (node != last) {
let next = node.next
if (node.value > next.value) { // swap
[node.value, next.value] = [next.value, node.value];
}
node = next;
}
last = last.prev; // shorten the range that must be bubbled through
}
}
* [Symbol.iterator]() {
let node = this.head;
while (node) {
yield node.value;
node = node.next;
}
}
toString() {
return Array.from(this).join(",");
}
}
// Demo
let list = new LinkedList([1, 10, 5, 7, 3, 2, 9, 8, 4, 6]);
console.log("before:", String(list));
list.bubbleSort();
console.log("after: ", String(list));
Note that it performs the swap by swapping the values, not the nodes. This is a cheaper operation.
class Node {
next: Node | null = null; //setting default value as null
constructor(public data: number) {}
}
export class LinkedList {
head: Node | null = null;
// even though I say void but we are still use "return". void means we are not returning any specific value.
add(data: number): void {
const node = new Node(data);
if (!this.head) {
this.head = node;
return;
}
let tail = this.head;
// initially "tail=head" but if tail or head has "next" property, then this next is the tail
while (tail.next) {
tail = tail.next;
}
// By default node.next=null
tail.next = node;
}
// length is optional. it might be helpful in some cases
get length(): number {
if (!this.head) {
return 0;
}
let length = 1;
let node = this.head;
while (node.next) {
length++;
node = node.next;
}
return length;
}
// we use this when swapping
at(index: number): Node {
if (!this.head) {
throw new Error("index out of bounds");
}
let counter = 0;
let node: Node | null = this.head;
while (node) {
if (counter === index) {
return node;
}
counter++;
node = node.next;
}
// if we hit out of while loop, we throw error. Otherwise ts would give warning because we should be returning "Node"
throw new Error("INdex out of bounds");
}
compare(leftIndex: number, rightIndex: number): boolean {
if (!this.head) {
throw new Error("List is empty");
}
return this.at(leftIndex).data > this.at(rightIndex).data;
}
swap(leftIndex: number, rightIndex: number): void {
//rather than just swapping nodes, we find the previous ones and change the next values
const leftNode = this.at(leftIndex);
const rightNode = this.at(rightIndex);
const leftHand = leftNode.data;
leftNode.data = rightNode.data;
rightNode.data = leftHand;
}
print(): void {
if (!this.head) {
return;
}
let node: Node | null = this.head;
while (node) {
console.log(node.data);
// this return error because "let node = this.head" ts infers that type of node will be Node but since "node=node.next" and node.next can be null, it shows error
node = node.next;
}
}
}

How to implement forEach loop for a javascript linked list

I have a LinkedList class in Javascript(ES6) which has all the related helper methods (like insert, remove, update etc). Additionally, I want to implement a reusable forEach loop that can be used to iterate over the linked list.
class LinkedList(){
constructor(){
//linked list
this.list = null;
}
//Inserting a node at the first position
insertFirst(data) {
let node = new Node(data);
if (this.list) {
node.next = this.list;
this.list = node;
}
else {
this.list = node;
}
}
//...
//Other helper methods...
//...
forEach(){
//loop implementation goes here
}
}
//Node class for creating a new Node
class Node {
constructor(data, next = null) {
this.data = data;
this.next = next;
}
}
For instance, this is the linked list with 'n' nodes,
this.list = {
data: 10,
next: node1
};
let node1 = {
data: 20,
next: node2
};
let node2 = {
data: 20,
next: node3
};
So and so...
I should be able to iterate the list like an array forEach and perform any action at the node level, like below
const list = new LinkedList();
//inserted 10 nodes using list.insertFirst() method
list.forEach(node => {
console.log(node.data);
});
What is the best possible way to achieve this?
I would define an iterator on the class. This way you can even iterate over the values with a for..of loop.
Then you can build the forEach method on top of that. But in many situations you'll find it easier to use the iterator instead of the forEach method, as you don't need to provide a callback function when using the iterator.
Here is how that could look:
class Node {
constructor(data, next=null) {
this.data = data;
this.next = next;
}
}
class LinkedList {
constructor() {
this.list = null;
}
insertFirst(data) {
this.list = new Node(data, this.list);
return this; // add this line to make the method chainable!
}
* [Symbol.iterator]() { // define the default iterator for this class
let node = this.list; // get first node
while (node) { // while we have not reached the end of the list
yield node.data; // ... yield the current node's data
node = node.next; // and move to the next node
}
}
forEach(cb) { // Takes a callback argument.
// As this object has a default iterator (see above) we
// can use the following syntax to consume that iterator.
// Call the callback for each yielded value.
for (let data of this) cb(data);
}
}
let list = new LinkedList;
// Insert a few values. We makde the insertFirst method chainable:
list.insertFirst(30).insertFirst(20).insertFirst(10);
// Pass console.log as callback to call for each data in the list
list.forEach(console.log);
// ... or just with the iterator:
for (let data of list) console.log(data);

Categories

Resources