I need help figuring out how to make a JavaScript algorithm that count the values of a stack, I am given 3 custom methods
stack - a Stack object containing zero or more values.
.pop() which pops the top value of the stack
.push() which pushes a value on to the stack
.peek() which shows me the top value of the stack without modifying the stack
I tried simply returning the length of said stack like so
function countValues(stack) {
return stack.length
}
but i get back undefined thus having no success
this is the stack class that was used to implement the custom methods
class Stack {
constructor(...values) {
const data = {};
let index = 0;
this.push = function (value) {
if (arguments.length < 1) {
throw new TypeError('stack.push() requires a value argument');
}
if (typeof value === 'undefined') {
throw new TypeError('stack.push(value) received undefined');
}
data[index] = value;
index++;
};
this.pop = function () {
const last = index - 1;
if (last < 0) return;
const value = data[last];
delete data[last];
index = last;
return value;
};
this.peek = function () {
const last = index - 1;
if (last < 0) return;
return data[last];
};
this.print = function () {
if (index === 0) {
return 'Stack { <empty> }';
}
let output = ' }';
let last = index - 1;
for (; last > 0; last--) {
output = ' <- ' + JSON.stringify(data[last]) + output;
}
output = JSON.stringify(data[last]) + output;
return 'Stack { ' + output;
};
for (let i = 0; i < values.length; i++) {
this.push(values[i]);
}
Object.freeze(this);
}
}
class Stack {
constructor(...values) {
const data = {};
let index = 0;
this.push = function(value) {
if (arguments.length < 1) {
throw new TypeError('stack.push() requires a value argument');
}
if (typeof value === 'undefined') {
throw new TypeError('stack.push(value) received undefined');
}
data[index] = value;
index++;
};
this.pop = function() {
const last = index - 1;
if (last < 0) return;
const value = data[last];
delete data[last];
index = last;
return value;
};
this.peek = function() {
const last = index - 1;
if (last < 0) return;
return data[last];
};
this.print = function() {
if (index === 0) {
return 'Stack { <empty> }';
}
let output = ' }';
let last = index - 1;
for (; last > 0; last--) {
output = ' <- ' + JSON.stringify(data[last]) + output;
}
output = JSON.stringify(data[last]) + output;
return 'Stack { ' + output;
};
for (let i = 0; i < values.length; i++) {
this.push(values[i]);
}
Object.freeze(this);
}
}
const myStack = new Stack(1, 2, 3, 4, 5);
// Here's an easy but not so good way:
function countValues1(stack) {
return Array.from(stack.print()).filter(x => x === "<").length + 1;
}
// Here's another way, but it mutates the stack:
function countValues2(stack) {
let count = 0;
while (true) {
if (stack.pop() === undefined) {
break;
}
count++;
}
return count;
}
console.log(countValues1(myStack));
console.log(countValues2(myStack));
Without knowing exactly how stack works, or specifically what you need (are you allowed to modify the stack implementation? are you allowed to mutate the contents of the stack?) we'll struggle for a good answer, but the above should help get you maybe slightly closer perhaps.
Maybe this can help you doing a length function manually.
You should check how you declared your stack, .length is supposed to be working.
function countValues(stack) {
const tempStack = stack;
var length=0;
while(tempStack.pop()!=undefined){
length++;
}
return length;
}
This function allows you to run it and still keeping your data in the original stack.
length is a getter, which is essentially a function posing as a property. It will just return undefined if you call it on an object that doesn't implement it. If the exercise -- I assume that's what it is -- says you can only use the abovementioned methods, then you can't use object.length.
Related
Is this a memoïze function?
Secondly,
is it an efficient one?
function memoïze(func) {
var array = [];
return (value) => {
var index = array.findIndex((item) => item.lastValue == value);
if (index >= 0) {
return array[index].lastResult;
} else {
var result = func(value);
var obj = {
lastValue: value,
lastResult: result,
};
array.push(obj);
return obj.lastResult;
}
};
}
I do not like that am storing two things for each call!
Yes, it is a memoization function, but it's not an efficient one, because is uses an O(n) lookup into an array instead of using an O(1) Map lookup.
Here's a more modern efficient version:
function memoize(factory, ctx) {
const cache = new Map();
return function(key) {
if (!cache.has(key)) {
cache.set(key, factory.call(ctx, key));
}
return cache.get(key);
}
}
with sample usage:
var fib = memoize(n => n < 2 ? 1 : fib(n - 1) + fib(n - 2));
where the usage of the cache improves the performance of the algorithm from O(1.6 ^ n) to O(n) for new values, and O(1) for previously calculated values, while still preserving the recursive nature of the algorithm.
NB: for fib(50) this takes under 200µS on my laptop whereas the non-memoized version takes over three minutes.
What you have here is a closure.
Your array doesnt get garbage collected because it still has an reference to your inner function.
You have a memoization, but it can be improved.
To make it more efficient i would go with an object witch has an access time of O(1) while you have an access time of O(n):
function memorise(func) {
var cache = {};
return (value) => {
if(value in cache) return cache[value];
var result = func(value);
cache[value] = result;
return result;
}
};
Example witch object:
function memorise() {
var cache = {};
return (value) => {
if(value in cache) return cache[value];
cache[value] = value;
return value;
}
};
let mem = memorise();
console.time('chrono 1');
for(let i = 0; i < 10000; i++) {
mem(i);
}
console.timeEnd('chrono 1');
console.time('chrono 2');
for(let i = 0; i < 10000; i++) {
mem(i);
}
console.timeEnd('chrono 2');
Example with array:
function memorise() {
var array = [];
return (value) => {
var index = array.findIndex((item) => item.lastValue == value);
if (index >= 0) {
return array[index].lastResult;
} else {
var obj = {
lastValue: value,
lastResult: "someval",
};
array.push(obj);
return obj.lastResult;
}
};
}
let mem = memorise();
for(let i = 0; i < 100000; i++) {
mem(i);
}
console.time();
for(let i = 0; i < 100000; i++) {
mem(i);
}
console.timeEnd();
as it is a concept of programming which I did not know, I made some personal tests on this type of coding.
Here is the result of these tests: a universal memoïze function?
const square = n => Math.pow(n, 2)
, cube = n => Math.pow(n, 3)
, multply = (a,b) => a * b
;
function memoïze(func)
{
let cache = {}
return (...vals) => {
let ref = JSON.stringify(vals)
if(ref in cache) return cache[ref]
// console.log('no cache for', ref)
let res = func(...vals)
cache[ref] = res
return res
}
}
let m_square = memoïze( square )
let m_cube = memoïze( cube )
let m_multply = memoïze( multply )
console.log( m_square(8)) // 64
console.log( m_cube(5) ) // 125
console.log( m_multply(3,7) ) // 21
console.log( '-- result with cache: --' )
console.log( m_square(8)) // 64
console.log( m_cube(5) ) // 125
console.log( m_multply(3,7) ) // 21
.as-console-wrapper { max-height: 100% !important; top: 0; }
the same with Alnitak method (with map)
function memoïze(func)
{
let cache = new Map()
return (...vals)=>
{
let key = JSON.stringify(vals)
if (!cache.has(key)) cache.set(key, func(...vals))
return cache.get(key)
} }
I am working on a project to generate random data structures for testing solutions for DSA problems. I am trying to form an algorithm that generates a random tree data structure that takes in the input of number of test cases and number of nodes. Since I cannot use pointers and references, I'm having trouble figuring out how to do this in javaScript.
So far I managed to get the basics down, however, I'm getting errors in my code
CODE:
const randnumgen = (min, max) => {
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min + 1)) + min;
};
function randtreegen(nodes) {
var string = "";
class Tree {
constructor(nodes) {
this.nodes = nodes;
this.adj = [];
}
addEdge(n, w) {
this.adj[n].push(w);
}
removeEdge(n, w) {
this.adj[n].forEach((elem) => {
if (elem === w) {
var index = this.adj[n].indexOf(elem);
if (index !== -1) this.adj[n].splice(index, 1);
}
});
}
isCyclicUtil(nodes, visited, recStack) {
if (visited[nodes] === false) {
visited[nodes] = true;
recStack[nodes] = true;
this.adj[n].forEach((elem) => {
if (!visited[elem] && this.isCyclicUtil(elem, visited, recStack))
return true;
else if (recStack[elem])
return true;
});
}
recStack[nodes] = false;
return false;
}
isCyclic() {
visited = new Array();
recStack = new Array();
for (var i = 0; i < this.nodes; i++) {
visited[i] = false;
recStack[i] = false;
}
for (var j = 0; j < this.nodes; i++) {
if (this.isCyclicUtil(j, visited, recStack))
return true;
}
return false;
}
}
container = new Set();
let t = new Tree(nodes);
for (var i = 1; i < nodes - 1; i++) {
var a = randnumgen(1, nodes);
var b = randnumgen(1, nodes);
var p = [a, b];
t.addEdge(p[0], p[1]);
while (container.has(`${p[0]},${p[1]}`) || t.isCyclic() === true) {
t.removeEdge(p[0], p[1]);
var a = randnumgen(1, nodes);
var b = randnumgen(1, nodes);
var p = [a, b];
t.addEdge(p[0], p[1]);
}
container.add(`${p[0]},${p[1]}`);
}
container.forEach((elem) => {
string += elem + '\n'
});
return string;
}
function treeGen(test_case, tree_nodes) {
var result = "";
while (test_case-- > 0) {
result += randtreegen(tree_nodes) + '\n';
}
return result;
}
const ans = treeGen(1, 5);
document.write(ans);
ERROR
TypeError: Cannot read property 'push' of undefined at /home/cg/root/7217808/main.js:18
this.adj[n].push(w);
My question is:
Is the Logic correct?
How to resolve the error to make it work?
P.S: I referred to this article on GeeksforGeeks.
The main issue is that you have not created the adj entries as empty arrays. So change:
this.adj = [];
To:
this.adj = Array.from({length: nodes}, () => []); // create that many empty arrays
But there are other issues as well:
Some pieces of code expect that nodes are numbered from 1, while other pieces of code expect a numbering starting at 0. As array indexes start from 0, it is more natural to also number your nodes starting from 0.
There are references to an unknown variable n, which should be nodes. NB: It is strange that you choose a plural name for this variable.
When you return true inside a forEach callback, you don't return from the outer function, but only from the forEach callback. This is not what you intended. Solve this by using a for...of loop.
In isCyclic you have a loop on j, but you increment with i++, so this loop will never end. Make it j++
The cycle test is not enough to ensure that your graph is a tree, because in a directed graph you can still have multiple paths between a node A and a node B, without cycles.
The loop in which edges are created needs one more iteration, so let it start from 0.
I would however suggest a slightly different approach for generating a random tree: shuffle all nodes randomly, and let the first node in that shuffled array be the root of the tree. Iterate all the other nodes, and let them be the destinations of new edges. Note that in a tree there is no node in which two edges arrive.
Then you can do a cycle test. I would however do this different too: perform a test before adding the edge. You can get all descendent nodes of the selected b node, and if a is in that set, then you should not create edge a,b.
Here is your adapted code. I removed the parts that are no longer used:
function randnumgen (min, max) {
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min + 1)) + min;
}
class Tree {
constructor(nodes) {
this.nodes = nodes;
this.adj = Array.from({length: nodes}, () => []);
}
addEdge(n, w) {
this.adj[n].push(w);
}
descendants(node) {
let visited = new Set([node]);
for (let node of visited) {
for (let elem of this.adj[node]) {
if (!visited.has(elem)) visited.add(elem);
}
}
return visited;
}
}
function shuffle(array) {
for (var i = array.length - 1; i > 0; i--) {
var j = Math.floor(Math.random() * (i + 1));
var temp = array[i];
array[i] = array[j];
array[j] = temp;
}
return array;
}
function randtreegen(nodes) {
let t = new Tree(nodes);
let [root, ...children] = shuffle([...Array(nodes).keys()]);
let edges = [];
let a;
for (let b of children) {
do {
a = randnumgen(0, nodes-1); // make zero based
} while (t.descendants(b).has(a));
t.addEdge(a, b);
edges.push([a, b]);
}
return edges.join("\n");
}
function treeGen(test_case, tree_nodes) {
var result = "";
while (test_case-- > 0) {
result += randtreegen(tree_nodes) + '\n';
}
return result;
}
const ans = treeGen(1, 5);
console.log(ans);
The following code is failing when I call it with isValid("()"). When I debugged it, I see that the comparision fails at parantheses[parantheses.length - 1] === p even though both the values are same. Can anyone tell why this is failing ?
var isValid = function(s) {
const openParantheses = new Set("(", "{", "[");
const closedParantheses = new Set(")", "}", "]");
const parantheses = [];
for (let p of s) {
if (openParantheses.has(p)) {
parantheses.push(p);
} else if (closedParantheses.has(p)) {
if (parantheses[parantheses.length - 1] === p) {
parantheses.pop();
} else {
return false;
}
}
}
if (parantheses.length > 0) {
return false;
}
return true;
};
console.log(isValid("()"))
Also when I do console.log(Inside If - ${parantheses}) This isnt printing the parantheses array as an array but just giving out values without array notation. Is this something expected ?
Thanks.
Since you never add closed parentheses to the array, the parantheses[parantheses.length - 1] === p clause is never true.
I would advise mapping closed parentheses to open ones and using this map to perform your check as I have done below.
Another thing: The Constructor of Set takes an array (or any iterable) of values. (source)
var isValid = function(testString) {
const openParantheses = new Set([
"(",
"{",
"["
]);
const closedParantheses = new Map([
[')', '('],
[']', '['],
['}', '{'],
]);
const parantheses = [];
for (const character of testString) {
if (openParantheses.has(character)) {
parantheses.push(character);
continue;
}
if (closedParantheses.has(character)) {
if (parantheses[parantheses.length - 1] === closedParantheses.get(character)) {
parantheses.pop();
continue;
}
return false;
}
}
return parantheses.length === 0;
};
console.log(isValid("()"));
console.log(isValid("[]"));
console.log(isValid("{}"));
console.log(isValid("([{}])"));
console.log(isValid("({[}])"));
var isValid = function(s) {
const openParantheses = "({[";
const closedParantheses = ")}]";
const parantheses = [];
for (let p of s) {
if (openParantheses.indexOf(p) >= 0) {
parantheses.push(p);
} else if (closedParantheses.indexOf(p) >= 0) {
if (openParantheses.indexOf(parantheses[parantheses.length - 1]) ===
closedParantheses.indexOf(p)) {
parantheses.pop();
} else {
return false;
}
}
}
if (parantheses.length > 0) {
return false;
}
return true;
};
console.log(isValid("()"))
I am attempting to produce the solution for the great MinStack problem and I am trying to write it using ES6 notation. I have been able to accomplish the problem using ES5 class notation and I cannot figure out what is wrong with my code. For some reason in my push method in my MinStack, the value I pass in becomes undefined. Thus when I print out min._min.storage all of the values are undefined. Any help is greatly appreciated and happy coding!
class Stack {
constructor(capacity) {
this.capacity = capacity;
this.count = 0;
this.storage = {};
}
push(value) {
console.log('here is this and your value', this, value) //value is undefined when _.min.push(value) is called
if (this.count === this.capacity) {
return 'Max capacity already reached. Remove element before adding a new one.';
} else {
this.storage[this.count++] = value;
}
}
// Time complexity: 0(1);
pop() {
var poppedElement = this.storage[--this.count];
delete this.storage[this.count];
if (this.count < 0) {
this.count = 0;
}
return poppedElement;
};
// Time complexity: 0(1)
peek() {
return this.storage[this.count - 1];
};
// Time complexity: 0(1)
count() {
return this.count;
};
}
// Time complexity: 0(1)
//1.
class MinStack extends Stack {
constructor(capacity) {
super(capacity);
this._min = new Stack(capacity);
}
push(value) {
if (this.count < this.capacity) {
if (value < this._min.peek()) {
this._min.push(value);
} else {
this._min.push(this._min.peek());
}
this.storage[this.count++] = value;
return this.count;
}
}
pop() {
const poppedElement = this.storage[--this.count];
this._min.pop();
delete this.storage[this.count];
if (this.count < 0) {
this.count = 0;
}
return poppedElement;
}
min() {
return this._min.peek();
}
peek() {
return this.storage[this.count - 1];
}
count() {
return this.count;
}
}
var min = new MinStack(6);
min.push(1);
min.push(2)
min.push(1)
min.push(0)
min.push(3)
min.push(5)
console.log(min);
console.log(min.min()) //undefined
var arr = ['test0','test2','test0'];
Like the above,there are two identical entries with value "test0",how to check it most efficiently?
If you sort the array, the duplicates are next to each other so that they are easy to find:
arr.sort();
var last = arr[0];
for (var i=1; i<arr.length; i++) {
if (arr[i] == last) alert('Duplicate : '+last);
last = arr[i];
}
This will do the job on any array and is probably about as optimized as possible for handling the general case (finding a duplicate in any possible array). For more specific cases (e.g. arrays containing only strings) you could do better than this.
function hasDuplicate(arr) {
var i = arr.length, j, val;
while (i--) {
val = arr[i];
j = i;
while (j--) {
if (arr[j] === val) {
return true;
}
}
}
return false;
}
There are lots of answers here but not all of them "feel" nice... So I'll throw my hat in.
If you are using lodash:
function containsDuplicates(array) {
return _.uniq(array).length !== array.length;
}
If you can use ES6 Sets, it simply becomes:
function containsDuplicates(array) {
return array.length !== new Set(array).size
}
With vanilla javascript:
function containsDuplicates(array) {
return array
.sort()
.some(function (item, i, items) {
return item === items[i + 1]
})
}
However, sometimes you may want to check if the items are duplicated on a certain field.
This is how I'd handle that:
containsDuplicates([{country: 'AU'}, {country: 'UK'}, {country: 'AU'}], 'country')
function containsDuplicates(array, attribute) {
return array
.map(function (item) { return item[attribute] })
.sort()
.some(function (item, i, items) {
return item === items[i + 1]
})
}
Loop stops when found first duplicate:
function has_duplicates(arr) {
var x = {}, len = arr.length;
for (var i = 0; i < len; i++) {
if (x[arr[i]]) {
return true;
}
x[arr[i]] = true;
}
return false;
}
Edit (fix 'toString' issue):
function has_duplicates(arr) {
var x = {}, len = arr.length;
for (var i = 0; i < len; i++) {
if (x[arr[i]] === true) {
return true;
}
x[arr[i]] = true;
}
return false;
}
this will correct for case has_duplicates(['toString']); etc..
var index = myArray.indexOf(strElement);
if (index < 0) {
myArray.push(strElement);
console.log("Added Into Array" + strElement);
} else {
console.log("Already Exists at " + index);
}
You can convert the array to to a Set instance, then convert to an array and check if the length is same before and after the conversion.
const hasDuplicates = (array) => {
const arr = ['test0','test2','test0'];
const uniqueItems = new Set(array);
return array.length !== uniqueItems.size();
};
console.log(`Has duplicates : ${hasDuplicates(['test0','test2','test0'])}`);
console.log(`Has duplicates : ${hasDuplicates(['test0','test2','test3'])}`);
Sorting is O(n log n) and not O(n). Building a hash map is O(n). It costs more memory than an in-place sort but you asked for the "fastest." (I'm positive this can be optimized but it is optimal up to a constant factor.)
function hasDuplicate(arr) {
var hash = {};
var hasDuplicate = false;
arr.forEach(function(val) {
if (hash[val]) {
hasDuplicate = true;
return;
}
hash[val] = true;
});
return hasDuplicate;
}
It depends on the input array size. I've done some performance tests with Node.js performance hooks and found out that for really small arrays (1,000 to 10,000 entries) Set solution might be faster. But if your array is bigger (like 100,000 elements) plain Object (i. e. hash) solution becomes faster. Here's the code so you can try it out for yourself:
const { performance } = require('perf_hooks');
function objectSolution(nums) {
let testObj = {};
for (var i = 0; i < nums.length; i++) {
let aNum = nums[i];
if (testObj[aNum]) {
return true;
} else {
testObj[aNum] = true;
}
}
return false;
}
function setSolution(nums) {
let testSet = new Set(nums);
return testSet.size !== nums.length;
}
function sortSomeSolution(nums) {
return nums
.sort()
.some(function (item, i, items) {
return item === items[i + 1]
})
}
function runTest(testFunction, testArray) {
console.log(' Running test:', testFunction.name);
let start = performance.now();
let result = testFunction(testArray);
let end = performance.now();
console.log(' Duration:', end - start, 'ms');
}
let arr = [];
let setSize = 100000;
for (var i = 0; i < setSize; i++) {
arr.push(i);
}
console.log('Set size:', setSize);
runTest(objectSolution, arr);
runTest(setSolution, arr);
runTest(sortSomeSolution, arr);
On my Lenovo IdeaPad with i3-8130U Node.js v. 16.6.2 gives me following results for the array of 1,000:
results for the array of 100,000:
Assuming all you want is to detect how many duplicates of 'test0' are in the array. I guess an easy way to do that is to use the join method to transform the array in a string, and then use the match method.
var arr= ['test0','test2','test0'];
var str = arr.join();
console.log(str) //"test0,test2,test0"
var duplicates = str.match(/test0/g);
var duplicateNumber = duplicates.length;
console.log(duplicateNumber); //2