Javascript: Detect and Remove a loop from cylic linked list - javascript

I have used Javascript to write a circular linked list and to detect and remove the loop.It is working fine untill the part of loop detection. How ever it is failing to remove the loopnode. More specifically: the removeLoop function of this code doesnot work.
Here is my code:
function Node(element){
this.element = element;
this.next = null;
}
//circular linked list class
function LList() {
this.head = new Node("head");
this.head.next = this.head;
this.find = find;
this.insert = insert;
this.display = display;
}
function find(item){
var curr = this.head;
while(curr.element != item){
curr = curr.next;
}
return curr;
}
//inserting items into linked list
function insert(newElem, after){
var newNode = new Node(newElem);
var curr = this.find(after);
newNode.next = curr.next;
curr.next = newNode;
}
function display() {
var currNode = this.head;
while ((currNode.next !== null) &&
(currNode.next.element !== "head")) {
console.log(currNode.next.element);
currNode = currNode.next;
}
}
function findPrevious(item){
var curr = this.head;
while(curr.next !== null && curr.next.element !== item){
curr =curr.next;
}
return curr;
}
//creating a linkedlist object
var furniture = new LList();
furniture.insert("chair","head");
furniture.insert("table", "chair");
furniture.insert("couch", "table");
furniture.insert("stool","couch");
//furniture.display();
//detecting if a linked list is circular
function detectALoop(list){
var slow = list.head;
var fast = list.head;
while(slow && fast && fast.next){
slow = slow.next;
fast = fast.next.next;
if(slow === fast){
removeLoop (slow, list);
return 1;
}
}
return 0;
}
//This part of the code doesnot work
function removeLoop(loopNode, list)
{
var ptr1 = loopNode;
var ptr2 = loopNode;
var looplen = 1,i;
// count the number of nodes in loop
while(ptr1.next != ptr2)
{
ptr1 = ptr1.next;
looplen++;
}
console.log(looplen)
ptr1 = list.head;
ptr2 = list.head;
for(i=0; i <= looplen; i++)
{
ptr2 = ptr2.next;
}
while(ptr2.next != ptr1.next)
{
ptr1 = ptr1.next;
ptr2 = ptr2.next;
}
ptr2.next = null; // breaking the loop
}
console.log(detectALoop(furniture))
furniture.display();

You are making this a lot more complicated than it needs to be if the loop has to be back onto the first element.
function breakLoop(list) {
var head = list.head, tail = head, len = 1;
while (tail.next != head) {
len++;
tail = tail.next;
}
tail.next = null;
console.log(len.toString());
}
Now if you may need to handle any arbitrary loop, I still have no idea what you need 3 loops for. Use an ES6 Set; most browsers now support this, I believe. I'm going to go ahead and return the length instead of logging it.
function breakLoopAnywhere(list) {
var seen = new Set, node = list.head;
while (!seen.has(node.next)) {
seen.add(node);
node = node.next;
}
node.next = null;
return seen.size;
}
If you don't have sets, you can hack it with an array, replacing has with indexOf and add with push.
If you feel you must have the ability to detect a loop vs a non-looping list without breaking it:
// takes a node, returns the node
// that points backwards on its next
function getLoopNode(node) {
var seen = new Set;
do {
seen.add(node);
} while (!seen.has(node.next) && node = node.next)
return node;
}
function detectLoop(node) {
return getLoopNode(node) != null;
}
function breakLoop(node) {
node = getLoopNode(node);
if (node) node.next = null;
}
Your detectALoop is less complicated, but it's wrong. The only loop this will detect is if node 2i loops back onto node i. But the list could be 3 elements long looping onto the start; it could be lots of numbers that aren't 2i and i. Since there are probably a lot of numbers, way too many to try them all, you can't fix this strategy. There is no clever way to find cycles in a graph that is any faster or more intuitive than the one I wrote above. As far as I know.

This variable is messed up...
var looplen = 1,i;
It looks like you want it to be a 1.

Your removeLoop code is wrong, it never terminates:
let's assume this list:
A -> B -> C -> A
with loop length 3.
You correctly find the loop length, 3, you then set ptr1 and ptr2 to the head of the list, and then call .next on ptr2 for the length of the loop + 1 times (because of <=).
// for i = 0; i <= 3
A.next -> B // i = 0
B.next -> C // i = 1
C.next -> A // i = 2
A.next -> B // i = 33
So in the end you have ptr2 = B and ptr1 = A, i.e. ptr2 === ptr1.next!
One is the next of the other, and in the while loop you advance both until one is equal to the other, but they will never be, because they always be one the next of the other!
If you change the <= to just < it works, but the second while loop is actually useless.

Related

faild to move the element in between?

i have been try to solve the sudoku with Blacktracking algo, everything is good, canvar is called and i able to see the number but the things is number are not moving i.e the logic is not exectuing
current.i === 0; is where i'm get the error! even i have declared a sperate variable for the num also the problem is not sloved. only if i remove the .num current == 0 than its not showing any error but still the number is not moving
enter image description here
var cell = [];
var stack = [];
var sudoku = [2,3,0,9,4,0,6,7,0,
8,0,0,3,2,5,9,1,4,
9,0,0,7,6,0,3,2,0,
1,0,0,0,0,0,7,9,2,
5,0,3,2,1,0,4,8,6,
4,0,0,6,8,0,5,3,1,
7,0,0,1,0,0,0,0,9,
6,5,9,8,7,2,1,4,3,
3,0,0,0,9,0,0,0,7];
var current;
var number = 1;
function setup(){
createCanvas(450,450);
var a=0;
var b=0;
for(var i=0;i<81;i++){
if(a%9==0 && i!=0){
b = b+50;
a = 0;
}
each[i] = new each(a,b,i,sudoku[i]);
a = a+50;
}
current = cell[0];
}
function draw(){
background(10);
for(var i=0;i<81;i++){
each[i].show();
}
if(current.num === 0){ //the error is typeerror can't read the property of num
if(! sameColumn(current.i,number) && ! sameRow(current.i,number) && ! sameSquare(current.i,number) && number<(10)){
current.num = number;
stack.push(current);
number = 0;
current.each[current.i+1];
}
else {
if(number > 8){
current.num = 0;
current = stack.pop();
number = current.num;
current.num = 0;
}
}
}
else{
current = each[current+1];
number = 0;
}
number++;
}
function each(a,b,i,num){
this.a = a;
this.b = b;
this.i = i;
this.num = num;
this.show = function(){
noFill();
stroke(255);
rect(this.a,this.b,50,50);
textSize(32);
text(this.num,a+12,b+40);
}
}
The error is pretty much straight forward. current = cell[0]; becomes undefined since you defined cell as an empty array and didn't manipulated it after that.
From what I have observed so far, many parts of your code logically does not work, for example,
same Column(current.i,number) && ! sameRow(current.i,number) && ! sameSquare(current.i,number)
will definitely throw you an error is it is executed (it is not since the execution does not reach to that line), unless you have a separate js file that contains these functions.
Another one is
current = cell[current+1];
if the current variable is to store the cell object, it does not make sense to add 1 to it, and vice versa.
Now I believe this is how setup function was meant to look like:
function setup(){
createCanvas(450,450);
var a=0;
var b=0;
for(var i=0;i<81;i++){
if(a%9==0 && i!=0){
b = b+50;
a = 0;
}
cell[i] = new Cell(a,b,i,sudoku[i]); //changed each[i] to cell[i], also renamed the 'each' class
a = a+50;
}
current = cell[0];
}
If possible, please edit in a little more information about what exactly does your code do. Cheers :)

Remove nodes from Linked List based on a value

I am working through a Hackerrank problem and I am trying to remove all nodes that are greater than a particular value.
This is their basic implementation
const SinglyLinkedListNode = class{
constructor(value) {
this.value = value;
this.next = null;
}
};
const SinglyLinkedList = class {
constructor() {
this.head = null;
this.tail = null;
}
insertNode(value) {
const node = new SinglyLinkedListNode(value);
if(!this.head) {
this.head = node;
} else {
this.tail.next = node;
}
this.tail = node;
}
};
My function to removeNodes is as follows...
SinglyLinkedList.prototype.removeNodes = function(listHead, x){
let currentNode = listHead;
while(currentNode !== null && currentNode.next !== null) {
let next = currentNode.next;
while (next !== null && next.value > x) {
next = next.next
}
currentNode.next = next
if(currentNode.next === null) {
break;
}
}
return currentNode
}
The parameters are: listhead - reference to the root node, and x - the integer value to filter the Linked List
So for example, the LL is 1-> 2 -> 4 -> 3 -> 5 and I need to remove all nodes greater than x (3) and maintain the integrity of the LL order.
The result should be 1 -> 2 -> 3.
I am getting confused on why I keep getting this.tail.value = 5 instead of
this.tail.value = 3, this.tail.next = null.
this is a REPL
Cause the tail has to be rewritten explicitly as it keeps a reference to an unlinked node otherwise. Before running the function the list looks like:
list: head tail
1 -> 2 -> 3 -> 4 -> 5
and afterwards the tail still points to 5 although that was unlinked:
list: head tail
1 -> 2 -> 3 5
To resolve this, just rewrite the tail at the end of your function:
return this.tail = currentNode;
Also you have to actually traverse the list, so add a
currentNode = currentNode.next;
at the end of the outer while.

NodeJS require with asynch functions when synch is wanted

I have the following code
var utils = require(`${__dirname}/../../utils/utils.js`);
...
let object = utils.parse(input);
if (object === undefined){
let helper = utils.recognize(input);
msg.channel.sendMessage("\"" + input + "\" not recognized. Did you mean \"" + helper[0] + "\"?");
object = utils.parse(helper[0]);
}
//code related to object
console.log(object.strLength);
where "parse" tries to match the input to an object in a database, and "recognize" tries to find the best match if the input is spelled incorrectly (Levenshtein) (along with additional info such as how close the match was).
Currently the issue is that the code is ran asynchronously; "object.strLength" returns an undefined before utils.recognize() returns a value. If I copy/paste the recognize() and parse() functions into the file, then the code is run synchronously and I do not run into any issues. However I would rather keep those functions in a separate file as I reuse them in other files.
Is there a way to specify that the functions in utils must be synch? I know that there are libraries that convert asynch into synch but I prefer to use as few libraries as I can help it. I tried to have the recognize functions return a Promise but it ended up as a jumbled mess
edit: here's parse. I did not think it was necessary to answer this question so I did not include it initially:
var db = require(`${__dirname}/../data/database.js`);
...
var parse = (input) => {
let output = db[output];
if (output === null) {
Object.keys(db).forEach((item) => {
if (db[item].num === parseInt(input) || (db[item].color + db[item].type === input)){
output = db[item];
return false;
}
});
}
return output;
}
I solved the issue, thanks everyone. Here's what was wrong, it was with recognize(). It was my mistake to not show the code for it initially.
Original recognize:
var recognize = (item) => {
//iterate through our databases and get a best fit
let bestItem = null;
let bestScore = 99999; //arbitrary large number
//let bestType = null;
//found algorithm online by milot-mirdita
var levenshtein = function(a, b) {
if (a.length == 0) { return b.length; }
if (b.length == 0) { return a.length; }
// swap to save some memory O(min(a,b)) instead of O(a)
if(a.length > b.length) {
let tmp = a;
a = b;
b = tmp;
}
let row = [];
for(let i = 0; i <= a.length; i++) {
row[i] = i;
}
for (let i = 1; i <= b.length; i++) {
let prev = i;
for (let j = 1; j <= a.length; j++) {
let val;
if (b.charAt(i-1) == a.charAt(j-1)) {
val = row[j-1]; // match
} else {
val = Math.min(row[j-1] + 1, // substitution
prev + 1, // insertion
row[j] + 1); // deletion
}
row[j - 1] = prev;
prev = val;
}
row[a.length] = prev;
}
return row[a.length];
}
//putting this here would make the code work
//console.log("hi");
Object.keys(db).forEach((key) => {
if (levenshtein(item, key) < bestScore) {
bestItem = key;
bestScore = levenshtein(item, key);
}
});
return [bestItem, bestScore];
}
My solution was to move the levenshtein function outside of the recognize function, so if I wanted to I can call levenshtein from another function
#user949300 and #Robert Moskal, I changed the forEach loop into a let...in loop. There is no functional difference (as far as I can tell) but the code does look cleaner.
#Thomas, I fixed the let output = db[output]; issue, oops.
Again, thanks for all of your help, I appreciate it. And happy New Year too

How to pick a random property from an object without repeating after multiple calls?

I'm trying to pick a random film from an object containing film objects. I need to be able to call the function repeatedly getting distinct results until every film has been used.
I have this function, but it doesn't work because the outer function returns with nothing even if the inner function calls itself because the result is not unique.
var watchedFilms = [];
$scope.watchedFilms = watchedFilms;
var getRandomFilm = function(movies) {
var moviesLength = Object.keys(movies).length;
function doPick() {
var pick = pickRandomProperty(movies);
var distinct = true;
for (var i = 0;i < watchedFilms.length; i += 1) {
if (watchedFilms[i]===pick.title) {
distinct = false;
if (watchedFilms.length === moviesLength) {
watchedFilms = [];
}
}
}
if (distinct === true) {
watchedFilms.push(pick.title);
return pick;
}
if (distinct === false) {
console.log(pick.title+' has already been picked');
doPick();
}
};
return doPick();
}
T.J. Crowder already gave a great answer, however I wanted to show an alternative way of solving the problem using OO.
You could create an object that wraps over an array and makes sure that a random unused item is returned everytime. The version I created is cyclic, which means that it infinitely loops over the collection, but if you want to stop the cycle, you can just track how many movies were chosen and stop once you reached the total number of movies.
function CyclicRandomIterator(list) {
this.list = list;
this.usedIndexes = {};
this.displayedCount = 0;
}
CyclicRandomIterator.prototype.next = function () {
var len = this.list.length,
usedIndexes = this.usedIndexes,
lastBatchIndex = this.lastBatchIndex,
denyLastBatchIndex = this.displayedCount !== len - 1,
index;
if (this.displayedCount === len) {
lastBatchIndex = this.lastBatchIndex = this.lastIndex;
usedIndexes = this.usedIndexes = {};
this.displayedCount = 0;
}
do index = Math.floor(Math.random() * len);
while (usedIndexes[index] || (lastBatchIndex === index && denyLastBatchIndex));
this.displayedCount++;
usedIndexes[this.lastIndex = index] = true;
return this.list[index];
};
Then you can simply do something like:
var randomMovies = new CyclicRandomIterator(Object.keys(movies));
var randomMovie = movies[randomMovies.next()];
Note that the advantage of my implementation if you are cycling through items is that the same item will never be returned twice in a row, even at the beginning of a new cycle.
Update: You've said you can modify the film objects, so that simplifies things:
var getRandomFilm = function(movies) {
var keys = Object.keys(movies);
var keyCount = keys.length;
var candidate;
var counter = keyCount * 2;
// Try a random pick
while (--counter) {
candidate = movies[keys[Math.floor(Math.random() * keyCount)]];
if (!candidate.watched) {
candidate.watched = true;
return candidate;
}
}
// We've done two full count loops and not found one, find the
// *first* one we haven't watched, or of course return null if
// they've all been watched
for (counter = 0; counter < keyCount; ++counter) {
candidate = movies[keys[counter]];
if (!candidate.watched) {
candidate.watched = true;
return candidate;
}
}
return null;
}
This has the advantage that it doesn't matter if you call it with the same movies object or not.
Note the safety valve. Basically, as the number of watched films approaches the total number of films, our odds of picking a candidate at random get smaller. So if we've failed to do that after looping for twice as many iterations as there are films, we give up and just pick the first, if any.
Original (which doesn't modify film objects)
If you can't modify the film objects, you do still need the watchedFilms array, but it's fairly simple:
var watchedFilms = [];
$scope.watchedFilms = watchedFilms;
var getRandomFilm = function(movies) {
var keys = Object.keys(movies);
var keyCount = keys.length;
var candidate;
if (watchedFilms.length >= keyCount) {
return null;
}
while (true) {
candidate = movies[keys[Math.floor(Math.random() * keyCount)]];
if (watchedFilms.indexOf(candidate) === -1) {
watchedFilms.push(candidate);
return candidate;
}
}
}
Note that like your code, this assumes getRandomFilm is called with the same movies object each time.

How do you implement a Stack and a Queue in JavaScript?

What is the best way to implement a Stack and a Queue in JavaScript?
I'm looking to do the shunting-yard algorithm and I'm going to need these data-structures.
var stack = [];
stack.push(2); // stack is now [2]
stack.push(5); // stack is now [2, 5]
var i = stack.pop(); // stack is now [2]
alert(i); // displays 5
var queue = [];
queue.push(2); // queue is now [2]
queue.push(5); // queue is now [2, 5]
var i = queue.shift(); // queue is now [5]
alert(i); // displays 2
taken from "9 JavaScript Tips You May Not Know"
Javascript has push and pop methods, which operate on ordinary Javascript array objects.
For queues, look here:
http://safalra.com/web-design/javascript/queues/
Queues can be implemented in
JavaScript using either the push and
shift methods or unshift and pop
methods of the array object. Although
this is a simple way to implement
queues, it is very inefficient for
large queues — because of the methods
operate on arrays, the shift and
unshift methods move every element in
the array each time they are called.
Queue.js is a simple and efficient queue implementation for JavaScript whose dequeue function runs in amortized constant time. As a result, for larger queues, it can be significantly faster than using arrays.
Arrays.
Stack:
var stack = [];
//put value on top of stack
stack.push(1);
//remove value from top of stack
var value = stack.pop();
Queue:
var queue = [];
//put value on end of queue
queue.push(1);
//Take first value from queue
var value = queue.shift();
If you wanted to make your own data structures, you could build your own:
var Stack = function(){
this.top = null;
this.size = 0;
};
var Node = function(data){
this.data = data;
this.previous = null;
};
Stack.prototype.push = function(data) {
var node = new Node(data);
node.previous = this.top;
this.top = node;
this.size += 1;
return this.top;
};
Stack.prototype.pop = function() {
temp = this.top;
this.top = this.top.previous;
this.size -= 1;
return temp;
};
And for queue:
var Queue = function() {
this.first = null;
this.size = 0;
};
var Node = function(data) {
this.data = data;
this.next = null;
};
Queue.prototype.enqueue = function(data) {
var node = new Node(data);
if (!this.first){
this.first = node;
} else {
n = this.first;
while (n.next) {
n = n.next;
}
n.next = node;
}
this.size += 1;
return node;
};
Queue.prototype.dequeue = function() {
temp = this.first;
this.first = this.first.next;
this.size -= 1;
return temp;
};
Here is my implementation of a stack and a queue using a linked list:
// Linked List
function Node(data) {
this.data = data;
this.next = null;
}
// Stack implemented using LinkedList
function Stack() {
this.top = null;
}
Stack.prototype.push = function(data) {
var newNode = new Node(data);
newNode.next = this.top; //Special attention
this.top = newNode;
}
Stack.prototype.pop = function() {
if (this.top !== null) {
var topItem = this.top.data;
this.top = this.top.next;
return topItem;
}
return null;
}
Stack.prototype.print = function() {
var curr = this.top;
while (curr) {
console.log(curr.data);
curr = curr.next;
}
}
// var stack = new Stack();
// stack.push(3);
// stack.push(5);
// stack.push(7);
// stack.print();
// Queue implemented using LinkedList
function Queue() {
this.head = null;
this.tail = null;
}
Queue.prototype.enqueue = function(data) {
var newNode = new Node(data);
if (this.head === null) {
this.head = newNode;
this.tail = newNode;
} else {
this.tail.next = newNode;
this.tail = newNode;
}
}
Queue.prototype.dequeue = function() {
var newNode;
if (this.head !== null) {
newNode = this.head.data;
this.head = this.head.next;
}
return newNode;
}
Queue.prototype.print = function() {
var curr = this.head;
while (curr) {
console.log(curr.data);
curr = curr.next;
}
}
var queue = new Queue();
queue.enqueue(3);
queue.enqueue(5);
queue.enqueue(7);
queue.print();
queue.dequeue();
queue.dequeue();
queue.print();
Javascript array shift() is slow especially when holding many elements. I know two ways to implement queue with amortized O(1) complexity.
First is by using circular buffer and table doubling. I have implemented this before. You can see my source code here
https://github.com/kevyuu/rapid-queue
The second way is by using two stack. This is the code for queue with two stack
function createDoubleStackQueue() {
var that = {};
var pushContainer = [];
var popContainer = [];
function moveElementToPopContainer() {
while (pushContainer.length !==0 ) {
var element = pushContainer.pop();
popContainer.push(element);
}
}
that.push = function(element) {
pushContainer.push(element);
};
that.shift = function() {
if (popContainer.length === 0) {
moveElementToPopContainer();
}
if (popContainer.length === 0) {
return null;
} else {
return popContainer.pop();
}
};
that.front = function() {
if (popContainer.length === 0) {
moveElementToPopContainer();
}
if (popContainer.length === 0) {
return null;
}
return popContainer[popContainer.length - 1];
};
that.length = function() {
return pushContainer.length + popContainer.length;
};
that.isEmpty = function() {
return (pushContainer.length + popContainer.length) === 0;
};
return that;}
This is performance comparison using jsPerf
CircularQueue.shift() vs Array.shift()
http://jsperf.com/rapidqueue-shift-vs-array-shift
As you can see it is significantly faster with large dataset
The stack implementation is trivial as explained in the other answers.
However, I didn't find any satisfactory answers in this thread for implementing a queue in javascript, so I made my own.
There are three types of solutions in this thread:
Arrays - The worst solution, using array.shift() on a large array is very inefficient.
Linked lists - It's O(1) but using an object for each element is a bit excessive, especially if there are a lot of them and they are small, like storing numbers.
Delayed shift arrays - It consists of associating an index with the array. When an element is dequeued, the index moves forward. When the index reaches the middle of the array, the array is sliced in two to remove the first half.
Delayed shift arrays are the most satisfactory solution in my mind, but they still store everything in one large contiguous array which can be problematic, and the application will stagger when the array is sliced.
I made an implementation using linked lists of small arrays (1000 elements max each). The arrays behave like delayed shift arrays, except they are never sliced: when every element in the array is removed, the array is simply discarded.
The package is on npm with basic FIFO functionality, I just pushed it recently. The code is split into two parts.
Here is the first part
/** Queue contains a linked list of Subqueue */
class Subqueue <T> {
public full() {
return this.array.length >= 1000;
}
public get size() {
return this.array.length - this.index;
}
public peek(): T {
return this.array[this.index];
}
public last(): T {
return this.array[this.array.length-1];
}
public dequeue(): T {
return this.array[this.index++];
}
public enqueue(elem: T) {
this.array.push(elem);
}
private index: number = 0;
private array: T [] = [];
public next: Subqueue<T> = null;
}
And here is the main Queue class:
class Queue<T> {
get length() {
return this._size;
}
public push(...elems: T[]) {
for (let elem of elems) {
if (this.bottom.full()) {
this.bottom = this.bottom.next = new Subqueue<T>();
}
this.bottom.enqueue(elem);
}
this._size += elems.length;
}
public shift(): T {
if (this._size === 0) {
return undefined;
}
const val = this.top.dequeue();
this._size--;
if (this._size > 0 && this.top.size === 0 && this.top.full()) {
// Discard current subqueue and point top to the one after
this.top = this.top.next;
}
return val;
}
public peek(): T {
return this.top.peek();
}
public last(): T {
return this.bottom.last();
}
public clear() {
this.bottom = this.top = new Subqueue();
this._size = 0;
}
private top: Subqueue<T> = new Subqueue();
private bottom: Subqueue<T> = this.top;
private _size: number = 0;
}
Type annotations (: X) can easily be removed to obtain ES6 javascript code.
You can use your own customize class based on the concept, here the code snippet which you can use to do the stuff
/*
* Stack implementation in JavaScript
*/
function Stack() {
this.top = null;
this.count = 0;
this.getCount = function() {
return this.count;
}
this.getTop = function() {
return this.top;
}
this.push = function(data) {
var node = {
data: data,
next: null
}
node.next = this.top;
this.top = node;
this.count++;
}
this.peek = function() {
if (this.top === null) {
return null;
} else {
return this.top.data;
}
}
this.pop = function() {
if (this.top === null) {
return null;
} else {
var out = this.top;
this.top = this.top.next;
if (this.count > 0) {
this.count--;
}
return out.data;
}
}
this.displayAll = function() {
if (this.top === null) {
return null;
} else {
var arr = new Array();
var current = this.top;
//console.log(current);
for (var i = 0; i < this.count; i++) {
arr[i] = current.data;
current = current.next;
}
return arr;
}
}
}
and to check this use your console and try these line one by one.
>> var st = new Stack();
>> st.push("BP");
>> st.push("NK");
>> st.getTop();
>> st.getCount();
>> st.displayAll();
>> st.pop();
>> st.displayAll();
>> st.getTop();
>> st.peek();
There are quite a few ways in which you can implement Stacks and Queues in Javascript. Most of the answers above are quite shallow implementations and I would try to implement something more readable (using new syntax features of es6) and robust.
Here's the stack implementation:
class Stack {
constructor(...items){
this._items = []
if(items.length>0)
items.forEach(item => this._items.push(item) )
}
push(...items){
//push item to the stack
items.forEach(item => this._items.push(item) )
return this._items;
}
pop(count=0){
//pull out the topmost item (last item) from stack
if(count===0)
return this._items.pop()
else
return this._items.splice( -count, count )
}
peek(){
// see what's the last item in stack
return this._items[this._items.length-1]
}
size(){
//no. of items in stack
return this._items.length
}
isEmpty(){
// return whether the stack is empty or not
return this._items.length==0
}
toArray(){
return this._items;
}
}
And this is how you can use the stack :
let my_stack = new Stack(1,24,4);
// [1, 24, 4]
my_stack.push(23)
//[1, 24, 4, 23]
my_stack.push(1,2,342);
//[1, 24, 4, 23, 1, 2, 342]
my_stack.pop();
//[1, 24, 4, 23, 1, 2]
my_stack.pop(3)
//[1, 24, 4]
my_stack.isEmpty()
// false
my_stack.size();
//3
If you would like to see the detailed description about this implementation and how it can be further improved, you can read here : http://jschap.com/data-structures-in-javascript-stack/
Here's the code for queue implementation in es6 :
class Queue{
constructor(...items){
//initialize the items in queue
this._items = []
// enqueuing the items passed to the constructor
this.enqueue(...items)
}
enqueue(...items){
//push items into the queue
items.forEach( item => this._items.push(item) )
return this._items;
}
dequeue(count=1){
//pull out the first item from the queue
this._items.splice(0,count);
return this._items;
}
peek(){
//peek at the first item from the queue
return this._items[0]
}
size(){
//get the length of queue
return this._items.length
}
isEmpty(){
//find whether the queue is empty or no
return this._items.length===0
}
}
Here's how you can use this implementation:
let my_queue = new Queue(1,24,4);
// [1, 24, 4]
my_queue.enqueue(23)
//[1, 24, 4, 23]
my_queue.enqueue(1,2,342);
//[1, 24, 4, 23, 1, 2, 342]
my_queue.dequeue();
//[24, 4, 23, 1, 2, 342]
my_queue.dequeue(3)
//[1, 2, 342]
my_queue.isEmpty()
// false
my_queue.size();
//3
To go through the complete tutorial of how these data structures have been implemented and how can these further be improved, you may want to go through the 'Playing with data structures in javascript' series at jschap.com . Here's the links for queues - http://jschap.com/playing-data-structures-javascript-queues/
I like to think that the cleanest way to implement stack and queues should be to use a container that allows addition and deletion from both ends and then limit its capabilities for one end which can be done through a simple array in Javascript.
// STATEMENTS USED IN STACK CONTAINER WHILE ENCAPSULATING
// Allow push and pop from the same end
array.push(element);
array.pop();
// STATEMENTS USED IN QUEUE CONTAINER WHILE ENCAPSULATING
// Allow push and pop from different ends
array.push(element);
array.shift();
Or else you can use two arrays to implement queue data structure.
var temp_stack = new Array();
var stack = new Array();
temp_stack.push(1);
temp_stack.push(2);
temp_stack.push(3);
If I pop the elements now then the output will be 3,2,1.
But we want FIFO structure so you can do the following.
stack.push(temp_stack.pop());
stack.push(temp_stack.pop());
stack.push(temp_stack.pop());
stack.pop(); //Pop out 1
stack.pop(); //Pop out 2
stack.pop(); //Pop out 3
/*------------------------------------------------------------------
Defining Stack Operations using Closures in Javascript, privacy and
state of stack operations are maintained
#author:Arijt Basu
Log: Sun Dec 27, 2015, 3:25PM
-------------------------------------------------------------------
*/
var stackControl = true;
var stack = (function(array) {
array = [];
//--Define the max size of the stack
var MAX_SIZE = 5;
function isEmpty() {
if (array.length < 1) console.log("Stack is empty");
};
isEmpty();
return {
push: function(ele) {
if (array.length < MAX_SIZE) {
array.push(ele)
return array;
} else {
console.log("Stack Overflow")
}
},
pop: function() {
if (array.length > 1) {
array.pop();
return array;
} else {
console.log("Stack Underflow");
}
}
}
})()
// var list = 5;
// console.log(stack(list))
if (stackControl) {
console.log(stack.pop());
console.log(stack.push(3));
console.log(stack.push(2));
console.log(stack.pop());
console.log(stack.push(1));
console.log(stack.pop());
console.log(stack.push(38));
console.log(stack.push(22));
console.log(stack.pop());
console.log(stack.pop());
console.log(stack.push(6));
console.log(stack.pop());
}
//End of STACK Logic
/* Defining Queue operations*/
var queue = (function(array) {
array = [];
var reversearray;
//--Define the max size of the stack
var MAX_SIZE = 5;
function isEmpty() {
if (array.length < 1) console.log("Queue is empty");
};
isEmpty();
return {
insert: function(ele) {
if (array.length < MAX_SIZE) {
array.push(ele)
reversearray = array.reverse();
return reversearray;
} else {
console.log("Queue Overflow")
}
},
delete: function() {
if (array.length > 1) {
//reversearray = array.reverse();
array.pop();
return array;
} else {
console.log("Queue Underflow");
}
}
}
})()
console.log(queue.insert(5))
console.log(queue.insert(3))
console.log(queue.delete(3))
Here is a fairly simple queue implementation with two aims:
Unlike array.shift(), you know this dequeue method takes constant time (O(1)).
To improve speed, this approach uses many fewer allocations than the linked-list approach.
The stack implementation shares the second aim only.
// Queue
function Queue() {
this.q = new Array(5);
this.first = 0;
this.size = 0;
}
Queue.prototype.enqueue = function(a) {
var other;
if (this.size == this.q.length) {
other = new Array(this.size*2);
for (var i = 0; i < this.size; i++) {
other[i] = this.q[(this.first+i)%this.size];
}
this.first = 0;
this.q = other;
}
this.q[(this.first+this.size)%this.q.length] = a;
this.size++;
};
Queue.prototype.dequeue = function() {
if (this.size == 0) return undefined;
this.size--;
var ret = this.q[this.first];
this.first = (this.first+1)%this.q.length;
return ret;
};
Queue.prototype.peek = function() { return this.size > 0 ? this.q[this.first] : undefined; };
Queue.prototype.isEmpty = function() { return this.size == 0; };
// Stack
function Stack() {
this.s = new Array(5);
this.size = 0;
}
Stack.prototype.push = function(a) {
var other;
if (this.size == this.s.length) {
other = new Array(this.s.length*2);
for (var i = 0; i < this.s.length; i++) other[i] = this.s[i];
this.s = other;
}
this.s[this.size++] = a;
};
Stack.prototype.pop = function() {
if (this.size == 0) return undefined;
return this.s[--this.size];
};
Stack.prototype.peek = function() { return this.size > 0 ? this.s[this.size-1] : undefined; };
If you understand stacks with push() and pop() functions, then queue is just to make one of these operations in the oposite sense. Oposite of push() is unshift() and oposite of pop() es shift().
Then:
//classic stack
var stack = [];
stack.push("first"); // push inserts at the end
stack.push("second");
stack.push("last");
stack.pop(); //pop takes the "last" element
//One way to implement queue is to insert elements in the oposite sense than a stack
var queue = [];
queue.unshift("first"); //unshift inserts at the beginning
queue.unshift("second");
queue.unshift("last");
queue.pop(); //"first"
//other way to do queues is to take the elements in the oposite sense than stack
var queue = [];
queue.push("first"); //push, as in the stack inserts at the end
queue.push("second");
queue.push("last");
queue.shift(); //but shift takes the "first" element
A little late answer but i think this answer should be here. Here is an implementation of Queue with O(1) enqueue and O(1) dequeue using the sparse Array powers.
Sparse Arrays in JS are mostly disregarded but they are in fact a gem and we should put their power in use at some critical tasks.
So here is a skeleton Queue implementation which extends the Array type and does it's things in O(1) all the way.
class Queue extends Array {
constructor(){
super()
Object.defineProperty(this,"head",{ value : 0
, writable: true
});
}
enqueue(x) {
this.push(x);
return this;
}
dequeue() {
var first;
return this.head < this.length ? ( first = this[this.head]
, delete this[this.head++]
, first
)
: void 0; // perfect undefined
}
peek() {
return this[this.head];
}
}
var q = new Queue();
console.log(q.dequeue()); // doesn't break
console.log(q.enqueue(10)); // add 10
console.log(q.enqueue("DIO")); // add "DIO" (Last In Line cCc R.J.DIO reis cCc)
console.log(q); // display q
console.log(q.dequeue()); // lets get the first one in the line
console.log(q.dequeue()); // lets get DIO out from the line
.as-console-wrapper {
max-height: 100% !important;
}
So do we have a potential memory leak here? No i don't think so. JS sparse arrays are non contiguous. Accordingly deleted items shouln't be a part of the array's memory footprint. Let the GC do it's job for you. It's free of charge.
One potential problem is that, the length property grows indefinitely as you keep enqueueing items to the queue. However still one may implement an auto refreshing (condensing) mechanism to kick in once the length reaches to a certain value.
Edit:
The above code if just fine but the delete operator, while still being O(1), is a slow one. Besides the modern JS engines are so optimized that for like < ~25000 items .shift() works O(1) anyways. So we need something better.
In this particular case, as the engines develop we must harness their new powers. The code below uses a linked list and i believe it is the fastest and safest modern JS Queue structure as of 2021.
class Queue {
#head;
#last;
constructor(){
this.#head;
this.#last;
};
enqueue(value){
var link = {value, next: void 0};
this.#last = this.#head ? this.#last.next = link
: this.#head = link;
}
dequeue(){
var first;
return this.#head && ( first = this.#head.value
, this.#head = this.#head.next
, first
);
}
peek(){
return this.#head && this.#head.value;
}
};
This is an extremely fast Queue structure and uses Private Class Fields to hide critical variables from prying eyes.
If you're looking for ES6 OOP implementation of Stack and Queue data-structure with some basic operations (based on linked lists) then it may look like this:
Queue.js
import LinkedList from '../linked-list/LinkedList';
export default class Queue {
constructor() {
this.linkedList = new LinkedList();
}
isEmpty() {
return !this.linkedList.tail;
}
peek() {
if (!this.linkedList.head) {
return null;
}
return this.linkedList.head.value;
}
enqueue(value) {
this.linkedList.append(value);
}
dequeue() {
const removedHead = this.linkedList.deleteHead();
return removedHead ? removedHead.value : null;
}
toString(callback) {
return this.linkedList.toString(callback);
}
}
Stack.js
import LinkedList from '../linked-list/LinkedList';
export default class Stack {
constructor() {
this.linkedList = new LinkedList();
}
/**
* #return {boolean}
*/
isEmpty() {
return !this.linkedList.tail;
}
/**
* #return {*}
*/
peek() {
if (!this.linkedList.tail) {
return null;
}
return this.linkedList.tail.value;
}
/**
* #param {*} value
*/
push(value) {
this.linkedList.append(value);
}
/**
* #return {*}
*/
pop() {
const removedTail = this.linkedList.deleteTail();
return removedTail ? removedTail.value : null;
}
/**
* #return {*[]}
*/
toArray() {
return this.linkedList
.toArray()
.map(linkedListNode => linkedListNode.value)
.reverse();
}
/**
* #param {function} [callback]
* #return {string}
*/
toString(callback) {
return this.linkedList.toString(callback);
}
}
And LinkedList implementation that is used for Stack and Queue in examples above may be found on GitHub here.
As many have said: native array using push and pop is fine for a stack, but using shift for taking elements from a queue means that the remaining elements need to move, which is potentially slow. The idea of using two stacks to make a queue in kevinyu's answer is a nice idea to fix it, and of course that can be done with native-array-stacks as well. (Edit: there was actually already an answer by Yuki-Dreamer that does this, albeit less compactly. I didn't notice it until now because it was unfairly downvoted.)
Here's a compact implementation using some ES5/ES6 features that makes a queue object which behaves as closely as possible to the native-push/shift variant, except that it takes amortized O(1) time per operation:
const queue = () => {
const a = [], b = [];
return {
push: (...elts) => a.push(...elts),
shift: () => {
if (b.length === 0) {
while (a.length > 0) { b.push(a.pop()) }
}
return b.pop();
},
get length() { return a.length + b.length }
}
}
Now you can do:
const q = queue();
q.push(8);
q.push(9);
q.push(10);
console.log(q.length); // outputs 3
console.log(q.shift()); // outputs 8
q.push(11);
console.log(q.shift()); // outputs 9
console.log(q.shift()); // outputs 10
console.log(q.shift()); // outputs 11
console.log(q.shift()); // outputs undefined
The queue implementation uses a getter syntax for length, to make it look like a property, and rest parameter syntax for push to allow pushing more than one thing at a time. If you don't want that, you can replace line 4 with push: elt => a.push(elt),. (Note, however, that you can not replace it with push: a.push, like I tried first myself with really weird results: that's because it results in the native push method being called with this set to the queue object.)
No Array(s)
//Javascript stack linked list data structure (no array)
function node(value, noderef) {
this.value = value;
this.next = noderef;
}
function stack() {
this.push = function (value) {
this.next = this.first;
this.first = new node(value, this.next);
}
this.pop = function () {
var popvalue = this.first.value;
this.first = this.first.next;
return popvalue;
}
this.hasnext = function () {
return this.next != undefined;
}
this.isempty = function () {
return this.first == undefined;
}
}
//Javascript stack linked list data structure (no array)
function node(value, noderef) {
this.value = value;
this.next = undefined;
}
function queue() {
this.enqueue = function (value) {
this.oldlast = this.last;
this.last = new node(value);
if (this.isempty())
this.first = this.last;
else
this.oldlast.next = this.last;
}
this.dequeue = function () {
var queuvalue = this.first.value;
this.first = this.first.next;
return queuvalue;
}
this.hasnext = function () {
return this.first.next != undefined;
}
this.isempty = function () {
return this.first == undefined;
}
}
Here is the linked list version of a queue that also includes the last node, as suggested by #perkins and as is most appropriate.
// QUEUE Object Definition
var Queue = function() {
this.first = null;
this.last = null;
this.size = 0;
};
var Node = function(data) {
this.data = data;
this.next = null;
};
Queue.prototype.enqueue = function(data) {
var node = new Node(data);
if (!this.first){ // for empty list first and last are the same
this.first = node;
this.last = node;
} else { // otherwise we stick it on the end
this.last.next=node;
this.last=node;
}
this.size += 1;
return node;
};
Queue.prototype.dequeue = function() {
if (!this.first) //check for empty list
return null;
temp = this.first; // grab top of list
if (this.first==this.last) {
this.last=null; // when we need to pop the last one
}
this.first = this.first.next; // move top of list down
this.size -= 1;
return temp;
};
The regular Array structure in Javascript is a Stack (first in, last out) and can also be used as a Queue (first in, first out) depending on the calls you make.
Check this link to see how to make an Array act like a Queue:
Queues
var x = 10;
var y = 11;
var Queue = new Array();
Queue.unshift(x);
Queue.unshift(y);
console.log(Queue)
// Output [11, 10]
Queue.pop()
console.log(Queue)
// Output [11]
Seems to me that the built in array is fine for a stack. If you want a Queue in TypeScript here is an implementation
/**
* A Typescript implementation of a queue.
*/
export default class Queue {
private queue = [];
private offset = 0;
constructor(array = []) {
// Init the queue using the contents of the array
for (const item of array) {
this.enqueue(item);
}
}
/**
* #returns {number} the length of the queue.
*/
public getLength(): number {
return (this.queue.length - this.offset);
}
/**
* #returns {boolean} true if the queue is empty, and false otherwise.
*/
public isEmpty(): boolean {
return (this.queue.length === 0);
}
/**
* Enqueues the specified item.
*
* #param item - the item to enqueue
*/
public enqueue(item) {
this.queue.push(item);
}
/**
* Dequeues an item and returns it. If the queue is empty, the value
* {#code null} is returned.
*
* #returns {any}
*/
public dequeue(): any {
// if the queue is empty, return immediately
if (this.queue.length === 0) {
return null;
}
// store the item at the front of the queue
const item = this.queue[this.offset];
// increment the offset and remove the free space if necessary
if (++this.offset * 2 >= this.queue.length) {
this.queue = this.queue.slice(this.offset);
this.offset = 0;
}
// return the dequeued item
return item;
};
/**
* Returns the item at the front of the queue (without dequeuing it).
* If the queue is empty then {#code null} is returned.
*
* #returns {any}
*/
public peek(): any {
return (this.queue.length > 0 ? this.queue[this.offset] : null);
}
}
And here is a Jest test for it
it('Queue', () => {
const queue = new Queue();
expect(queue.getLength()).toBe(0);
expect(queue.peek()).toBeNull();
expect(queue.dequeue()).toBeNull();
queue.enqueue(1);
expect(queue.getLength()).toBe(1);
queue.enqueue(2);
expect(queue.getLength()).toBe(2);
queue.enqueue(3);
expect(queue.getLength()).toBe(3);
expect(queue.peek()).toBe(1);
expect(queue.getLength()).toBe(3);
expect(queue.dequeue()).toBe(1);
expect(queue.getLength()).toBe(2);
expect(queue.peek()).toBe(2);
expect(queue.getLength()).toBe(2);
expect(queue.dequeue()).toBe(2);
expect(queue.getLength()).toBe(1);
expect(queue.peek()).toBe(3);
expect(queue.getLength()).toBe(1);
expect(queue.dequeue()).toBe(3);
expect(queue.getLength()).toBe(0);
expect(queue.peek()).toBeNull();
expect(queue.dequeue()).toBeNull();
});
Hope someone finds this useful,
Cheers,
Stu
Construct a Queue using two Stacks.
O(1) for both enqueue and dequeue operations.
class Queue {
constructor() {
this.s1 = []; // in
this.s2 = []; // out
}
enqueue(val) {
this.s1.push(val);
}
dequeue() {
if (this.s2.length === 0) {
this._move();
}
return this.s2.pop(); // return undefined if empty
}
_move() {
while (this.s1.length) {
this.s2.push(this.s1.pop());
}
}
}
Single-ended queue
Here is a queue using a map. Since insertion order is guaranteed, you can iterate it like an array. Other than that the idea is very similar to Queue.js.
I've made some simple tests, but haven't tested it extensively. I also added some features that I thought were nice (constructing via an array) or easy to implement (e.g. last() and first()).
The simple version / intuition behind it is below:
class Queue {
constructor() {
this.offset = 0
this.data = new Map()
}
enqueue(item) {
const current = this.offset + this.length()
this.data.set(current, item)
}
dequeue() {
if (this.length() > 0) {
this.data.delete(this.offset)
this.offset += 1
}
}
first() {
return this.data.get(this.offset)
}
last() {
return this.data.get(this.offset + this.length() - 1)
}
length() {
return this.data.size
}
}
The issue with the simple version is that memory needs to be remapped when it has indexed over about 9 quadrillion (the value of Number.MAX_SAFE_INTEGER). Moreover, I think it might be nice to have array construction and it is nice to see the values being enqueued and dequeued being returned. One could account for this by writing the following code:
class Queue {
constructor() {
this.offset = 0
this.data = new Map()
if (arguments.length === 1) this._initializeFromArray(arguments[0])
}
enqueue(item) {
const current = this.offset + this.length()
this.data.set(current, item)
let result = this.data.get(current)
this._remapDataIfMaxMemoryViolation(current, Number.MAX_SAFE_INTEGER)
return result
}
dequeue() {
let result = undefined
if (this.length() > 0) {
result = this.data.get(this.offset)
this.data.delete(this.offset)
this.offset += 1
}
if (this.length() === 0) this.offset = 0
return result
}
first() {
return this.data.get(this.offset)
}
last() {
return this.data.get(this.offset + this.length() - 1)
}
length() {
return this.data.size
}
_remapDataIfMaxMemoryViolation(current, threshhold) {
if (current+1 === threshhold) {
const length = this.length()
this.offset = 0
for (const [key, value] of this.data) {
this.data.set(this.offset, value)
this.data.delete(key, value)
this.offset += 1
if (this.offset === length) break
}
}
}
_initializeFromArray(array) {
for (const value of array) {
this.data.set(this.offset, value)
this.offset += 1
}
}
}
I did some testing in the Chrome developer console with the following calls on the full version.
l = console.log // I'm lazy with typing
q = new Queue()
l('enqueue', q.enqueue(1))
l('enqueue', q.enqueue(2))
l('enqueue', q.enqueue(3))
l('enqueue', q.enqueue("hello"))
l('enqueue', q.enqueue("monkey"))
l('show 5 elements: ', q.data)
l('length', q.length())
l('first', q.first())
l('last', q.last())
l('dequeue', q.dequeue())
l('dequeue', q.dequeue())
l('show 3 elements', q.data)
q._remapDataIfMaxMemoryViolation(q.length()+q.offset-1, 5)
l('show 3 remapped elements', q.data)
l(queue = new Queue([3,4,5,6,7,8,9]))
l(queue.data)
Sorry to bump this topic but I scrolled over the many answers and did not see any implementation of an Object based Queue, which can perform enqueue AND dequeue with O(1) AND no wasted memory.
Dmitri Pavlutin has a nice starter code on his blog https://dmitripavlutin.com/javascript-queue/
It only misses a 0 length check, which is trivial to add.
the big and only problem of this solution is the ever growing index wich could hit some Number limit at one point, if the queue runs for a long time and/or at high speed (my intent is to process audio = high speed).
There is no perfect solution for this... the easy way can be resetting the index to 0 whenever the queue is empty.
At Last, I added a refactor method which costly shifts all the indexes back to the beginning, to use in the case the queue is never empty.
The performance is with no doubt better (the number is time in miliseconds for enqueuing 10 000 numbers then dequeuing them) :
class QueueObject {
constructor () {
this.data = {}
this.head = 0
this.tail = 0
this.length = 0
}
enqueue (value) {
this.data[this.tail++] = value
this.length++
}
dequeue () {
let value
if (this.length > 0) {
this.length--
value = this.data[this.head]
delete this.data[this.head++]
} else {
this.head = 0
this.tail = 0
value = null
}
return value
}
refactor () {
if (this.head > 0) {
for (let i = this.head; i < this.tail; i++) {
this.data[i - this.head] = this.data[i]
delete this.data[i]
}
this.tail = this.length
this.head = 0
}
}
}
I ran into this thread while implementing a BFS. After wondering why the performance was so poor I did some research. array.shift() typically runs in O(n) which increases my BFS runtime from O(V+E) to O(V^2+E).
Instead of implementing a queue from scratch I used the npm package double-ended-queue which is compatible to the previously used array methods and works like a charm.
The deque can be used as stack or queue.
//import package
import Deque from 'double-ended-queue';
//create queue
let queue = new Deque();
//append
queue.push(item);
//dequeue (get first item inserted)
let firstItem = queue.shift();
//pop (get last item inserted)
let lastItem = queue.pop();
An array is a stack in Javascript. Just use arr.push(x) and y = arr.pop().
Here is the simplest method for implementing a queue in Javascript that has an amortized time of O(1) for both enqueue(x) and y = dequeue(). It uses a mapping from insertion index to element.
function newQueue() {
return {
headIdx: 0,
tailIdx: 0,
elts: {},
enqueue: (elt) => queue.elts[queue.tailIdx++] = elt,
dequeue: () => {
if (queue.headIdx == queue.tailIdx) {
throw new Error("Queue is empty");
}
return queue.elts[queue.headIdx++];
},
size: () => queue.tailIdx - queue.headIdx,
isEmpty: () => queue.tailIdx == queue.headIdx
};
}
A queue implemented using a linked list is more efficient than this map-based method, and a queue implemented using a circular buffer is much more efficient than this map-based method, but the implementations of those two data structures is more complicated (especially the circular buffer data structure).
Create a pair of classes that provide the various methods that each of these data structures has (push, pop, peek, etc). Now implement the methods. If you're familiar with the concepts behind stack/queue, this should be pretty straightforward. You can implement the stack with an array, and a queue with a linked list, although there are certainly other ways to go about it. Javascript will make this easy, because it is weakly typed, so you don't even have to worry about generic types, which you'd have to do if you were implementing it in Java or C#.
Here is my Implementation of Stacks.
function Stack() {
this.dataStore = [];
this.top = 0;
this.push = push;
this.pop = pop;
this.peek = peek;
this.clear = clear;
this.length = length;
}
function push(element) {
this.dataStore[this.top++] = element;
}
function peek() {
return this.dataStore[this.top-1];
}
function pop() {
return this.dataStore[--this.top];
}
function clear() {
this.top = 0;
}
function length() {
return this.top;
}
var s = new Stack();
s.push("David");
s.push("Raymond");
s.push("Bryan");
console.log("length: " + s.length());
console.log(s.peek());
you can use WeakMaps for implementing private property in ES6 class and benefits of String propeties and methods in JavaScript language like below:
const _items = new WeakMap();
class Stack {
constructor() {
_items.set(this, []);
}
push(obj) {
_items.get(this).push(obj);
}
pop() {
const L = _items.get(this).length;
if(L===0)
throw new Error('Stack is empty');
return _items.get(this).pop();
}
peek() {
const items = _items.get(this);
if(items.length === 0)
throw new Error ('Stack is empty');
return items[items.length-1];
}
get count() {
return _items.get(this).length;
}
}
const stack = new Stack();
//now in console:
//stack.push('a')
//stack.push(1)
//stack.count => 2
//stack.peek() => 1
//stack.pop() => 1
//stack.pop() => "a"
//stack.count => 0
//stack.pop() => Error Stack is empty

Categories