Non-repeating stack in JavaScript - javascript

I need to keep a stack of 10 items (value primitives, not objects) where no item is repeated. Here's my initial stab at an implementation. Any suggestions for improvements?
var items = [];
function newItem() {
return Math.floor(Math.random() * 50);
}
function inArray(arr, val) {
var in_arr, i;
in_arr = false;
i = arr.length;
if (i < 1) {
return in_arr;
}
do {
if (arr[i - 1] === val) {
in_arr = true;
break;
}
} while (--i);
return in_arr;
}
function addItem() {
var new_item;
while (items.length > 9) {
items.shift();
}
do {
new_item = newItem();
} while (inArray(items, new_item));
items.push(new_item);
}

while (items.length > 9) {
items.shift();
}
can be written without iteration as
var len = items.length;
if (len > 9) {
items = items.slice(len - 9);
}
Since JS 1.6, inArray can be written as array.indexOf(element) != -1. Otherwise,
if (i < 1) {
return in_arr;
}
do {
if (arr[i - 1] === val) {
in_arr = true;
break;
}
} while (--i);
return in_arr;
can be written more simply as
while (i--) {
if (arr[i] === val) {
return true;
}
}
return false;

function inArray(arr, val) {
return !!~arr.indexOf(val);
}
with this shim if needed
function addItem(item) {
if (inArray(items, item)) return false;
if (items.length > 9) items = items.slice(len - 9);
items.push(item);
return true;
}
I would rewrite the hold thing like this:
function stack10() {
var items = arguments || [];
this.addItem = function (item) {
var len = items.length;
if (!!~items.indexOf(item)) return;
if (len > 9) items = items.slice(len - 9);
items.push(item);
}
this.peek = function() {
if (items.length === 0) return null;
return items[items.length-1];
}
this.pop = function () {
return items.pop();
}
}
var myStack = new stack10();
myStack.addItem(10);
myStack.peek(); // 10
myStack.pop(); // 10
myStack.pop(); // undefined
// default values
var myStack2 = new stack10(1,2,3,4);
myStack2.addItem(10);
myStack2.peek(); // 10
myStack2.pop(); // 10
myStack2.pop(); // 4

Why not use a heap? You can do O(lg n) insert and removal instead of O(n), and it's still in-place.

Related

This palindrome code returns error only while submitting in leetcode, its working in my local for same testcase

This code return "false" for test input "1" only when I submit in leet code. It's working when I do it in my local or leet code editor for the same input
var temp = 0;
var rev = 0;
var palindromeCheck = function (org) {
temp = org % 10;
rev = rev * 10 + temp;
org = parseInt(org / 10);
if (org > 0) {
palindromeCheck(org);
}
return rev;
};
var isPalindrome = function (x) {
if (x == 0) {
return true;
}
else if(x > 0) {
var value = palindromeCheck(x);
if (value === x) {
return true;
}
else {
return false;
}
}
else {
return false;
}
};
your palindromeCheck() function return a number. The problem comes from this part, where you strictly compare a number and a string:
if (value === x) {
return true;
}
else {
return false;
}
try to do :
if (value == x) {
return true;
}
else {
return false;
}
or
return (value == x);
else you can just use parseInt() function.

implementing split() method for exercise 4 in Object Oriented Javascript 2n edition

here is the question:
Imagine the String() constructor didn't exist. Create a constructor
function, MyString(), that acts like String() as closely as possible.
You're not allowed to use any built-in string methods or properties,
and remember that String() doesn't exist. You can use this code to
test your constructor:
I created constructor however I have no clue how to re-create split method, how to implement that functionality.
If you could give an idea how to implement split method, I would be grateful
function MyString(str) {
var thisObj = this;
var innerLength = 0;
this.length;
function updateLength() {
innerLength = 0;
for (var i = 0; str[i] != undefined; i++) {
innerLength++;
thisObj[i] = str[i];
}
thisObj.length = innerLength;
}
updateLength();
this.toString = function() {
return str;
}
this.charAt = function(i) {
if (isNaN(parseInt(i))) {
return this[0]
} else {
return this[i]
}
}
this.concat = function(string) {
str += string;
updateLength();
}
this.slice = function(start, end) {
var slicedString = "";
if (start >= 0 && end >= 00) {
for (start; start < end; start++) {
slicedString += str[start];
}
}
return slicedString;
}
this.reverse = function() {
var arr = str.split("");
arr.reverse();
var reversedString = "",
i;
for (i = 0; i < arr.length; i++) {
reversedString += arr[i];
}
return reversedString;
}
}
var ms = new MyString("Hello, I am a string")
console.log(ms.reverse())
You can convert the string to an array and use Array.prototype and RegExp.prototype methods.
this.split = function(re) {
var arr = Array.prototype.slice.call(this.toString());
if (re === "") {
return arr
}
if (re === " ") {
return arr.filter(function(el) {
return /[^\s]/.test(el)
})
}
if (/RegExp/.test(Object.getPrototypeOf(re).constructor)) {
var regexp = re.source;
return arr.filter(function(el) {
return regexp.indexOf(el) === -1
})
}
}
function MyString(str) {
var thisObj = this;
var innerLength = 0;
this.length;
function updateLength() {
innerLength = 0;
for (var i = 0; str[i] != undefined; i++) {
innerLength++;
thisObj[i] = str[i];
}
thisObj.length = innerLength;
}
updateLength();
this.toString = function() {
return str;
}
this.split = function(re) {
var arr = Array.prototype.slice.call(this.toString());
if (re === "") {
return arr
}
if (re === " ") {
return arr.filter(function(el) {
return /[^\s]/.test(el)
})
}
if (/RegExp/.test(Object.getPrototypeOf(re).constructor)) {
var regexp = re.source;
return arr.filter(function(el) {
return regexp.indexOf(el) === -1
})
}
}
this.charAt = function(i) {
if (isNaN(parseInt(i))) {
return this[0]
} else {
return this[i]
}
}
this.concat = function(string) {
str += string;
updateLength();
}
this.slice = function(start, end) {
var slicedString = "";
if (start >= 0 && end >= 00) {
for (start; start < end; start++) {
slicedString += str[start];
}
}
return slicedString;
}
this.reverse = function() {
var arr = str.split("");
arr.reverse();
var reversedString = "",
i;
for (i = 0; i < arr.length; i++) {
reversedString += arr[i];
}
return reversedString;
}
}
var ms = new MyString("Hello, I am a string")
console.log(ms.split(""), ms.split(" "), ms.split(/l/))
Iterate and search:
MyString.prototype.split = function(splitter){
var tmp="", result = [];
for(var i = 0; i < this.length; i++){
for(var offset = 0; offset < this.length && offset < splitter.length;offset++){
if(this[i+offset] !== splitter[offset]) break;
}
if(offset === splitter.length){
result.push( tmp );
tmp="";
i += offset -1;
}else{
tmp+=this[i];
}
}
result.push(tmp);
return result;
};
So now you can do:
new MyString("testtest").split("t") //['','es','','','es','']
In action

Last Key in JavaScript array appearing as Length?

Bit of a weird one. Am using the following code build an array from a json object to make it easier to reference later in the code. However it would appear that when the last item of each array is created, rather than adding a new item, the Key of the item appears as the length of the array.
perfsJson = $.parseJSON(result);
var extras = new Array();
for (var i = perfsJson.length - 1; i >= 0; i--) {
var obj = perfsJson[i];
if (obj != null) {
if (obj.Extras != null) {
for (var perf_no in obj.Extras) {
if (extras[perf_no] == undefined) {
var arr = new Array();
for (var extra in obj.Extras[perf_no]) {
if (arr[extra] == undefined) {
arr[extra] = obj.Extras[perf_no][extra];
}
}
extras[perf_no] = arr;
}
}
break;
}
}
}
The resulting array appears as below:
Any ideas what's going on here?
Edit:
Sample of Json below
{"Extras":{"32516":{"24186":"Example text"},"32515":{"24186":"Example text"},"32514":{"24186":"Example text"},"32512":{"24186":"Example text"},"32513":{"24186":"Example text"},"32511":{"24186":"Example text"},"32510":{"24186":"Example text"},"32509":{"24186":"Example text"},"32507":{"24186":"Example text"},"32503":{"24186":"Example text"},"32506":{"24186":"Example text"},"32505":{"24186":"Example text"},"32508":{"24186":"Example text"},"32502":{},"32497":{}}}
What's going on hear is that you are using for..in to iterate over an array, which is a no-no because it iterates properties that are not the array elements (such as the .length property). Instead, use Array#forEach:
perfsJson = $.parseJSON(result);
var extras = new Array();
for (var i = perfsJson.length - 1; i >= 0; i--) {
var obj = perfsJson[i];
if (obj != null) {
if (obj.Extras != null) {
obj.Extras.forEach(function (item, idx) {
if (typeof extras[idx] === 'undefined') {
var arr = new Array();
item.forEach(function (item2, idx2) {
if (typeof arr[idx2] === 'undefined') {
arr[idx2] = item2;
}
});
extras[idx] = arr;
}
});
break;
}
}
}
The innermost loop is pretty pointless and can be replaced with Array#slice:
perfsJson = $.parseJSON(result);
var extras = new Array();
for (var i = perfsJson.length - 1; i >= 0; i--) {
var obj = perfsJson[i];
if (obj != null) {
if (obj.Extras != null) {
obj.Extras.forEach(function (item, idx) {
if (typeof extras[idx] === 'undefined') {
extras[idx] = item.slice();
}
});
break;
}
}
}
The next inner loop can be replaced with Array#map and two if statements can be combined:
perfsJson = $.parseJSON(result);
var extras = new Array();
for (var i = perfsJson.length - 1; i >= 0; i--) {
var obj = perfsJson[i];
if (obj != null&& obj.Extras != null) {
extras = obj.Extras.map(function (item) {
return item.slice();
});
break;
}
}
In fact, most of this code can be simplified:
function findLastElement(arr) {
for (var i = arr.length - 1; i >= 0; i -= 1) {
if (arr[i] != null && arr[i].Extras != null) { return arr[i]; }
}
}
perfsJson = $.parseJSON(result);
var lastElement = findLastElement(perfsJson);
var extras = lastElement
? lastElement.Extras.map(function (item) { return item.slice(); })
: [];

Angular 1.5 custom filter return data issue

module.filter('myCustomFilter', function ($filter) {
return function(items, searchedTxt, headers) {
if (headers.choice === "option1") {
return resultByDates(items, searchedTxt);
} else if (headers.choice === "option2") {
return resultByName(items, searchedTxt, headers);
}
else if (headers.choice === "option3") {
return resultSimple(items, searchedTxt);
}
return items;
};
function resultByDates(items, search) {
if (search === undefined || search === null)
return items;
var k = Object.keys(search)[0];
var i;
for (i = 0; i < items.length; i++) {
items[i][k] = $filter('date')(items[i][k], "MM/dd/yyyy");
}
var filteredData = $filter('filter')(items, search);
var indexes = [];
for (i = 0; i < filteredData.length; i++) {
indexes.push(items.indexOf(filteredData[i]));
}
var output = [];
for (i = 0; i < indexes.length; i++) {
output.push(items[indexes[i]]);
}
return output;
}
function resultByName(items, search, headers) {
if (search === undefined || search === null)
return items;
if (headers !== undefined) {
var k = Object.keys(search)[0];
var i;
var componentVals = headers.componentVals;
var itemsCopy = angular.copy(items);
for (i = 0; i < items.length; i++) {
for (var obj in componentVals) {
if (componentVals[obj].ID === itemsCopy[i][k]) {
itemsCopy[i][k] = componentVals[obj].Name;
break;
}
}
}
var filteredData = $filter('filter')(itemsCopy, search);
var indexes = [];
for (i = 0; i < filteredData.length; i++) {
indexes.push(itemsCopy.indexOf(filteredData[i]));
}
var output = [];
for (i = 0; i < indexes.length; i++) {
output.push(items[indexes[i]]);
}
return output;
}
return items;
}
function resultSimple(items, search) {
if (search === undefined || search === null)
return items;
return $filter('filter')(items, search);
}
});
Guys, I have above filter which works - partially, Option1 and Option3 returns correct filtered data, but there is some problem with Option2.
When I filter data with Option1 it returns correctly filtered data, then I can additionally filter with Option3 and it filters incorrectly returned previously data. When I use Option2 it seems like the data is being returned is not binded with the previous return, it's returning separate data.It seems like it is a separate collection... Is there something wrong with the way I return data in Option2?
Hope I have explained problem sufficiently. Thanks.
Below version with some fixes suggested by Himmel.
module.filter('myCustomFilter', function ($filter) {
return function (items, searchedTxt, headers) {
var key;
var i;
var indexes;
var filteredData;
var output;
if (headers.choice === "option1") {
key = Object.keys(searchedTxt)[0];
for (i = 0; i < items.length; i++) {
items[i][key] = $filter('date')(items[i][key], "MM/dd/yyyy");
}
filteredData = $filter('filter')(items, searchedTxt);
indexes = [];
for (i = 0; i < filteredData.length; i++) {
indexes.push(items.indexOf(filteredData[i]));
}
output = [];
for (i = 0; i < indexes.length; i++) {
output.push(items[indexes[i]]);
}
return output;
} else if (headers.choice === "option2") {
key = Object.keys(searchedTxt)[0];
var componentVals = headers.componentVals;
var itemsCopy = angular.copy(items);
for (i = 0; i < items.length; i++) {
for (var obj in componentVals) {
if (componentVals[obj].ID === itemsCopy[i][key]) {
itemsCopy[i][key] = componentVals[obj].Name;
break;
}
}
}
filteredData = $filter('filter')(itemsCopy, searchedTxt);
indexes = [];
for (i = 0; i < filteredData.length; i++) {
indexes.push(itemsCopy.indexOf(filteredData[i]));
}
output = [];
for (i = 0; i < indexes.length; i++) {
output.push(items[indexes[i]]);
}
return output;
} else if (headers.choice === "option3") {
return $filter('filter')(items, searchedTxt);
}
return items;
};
});
I have finally figured out the problem… So when I filtered with Option3 there was no issue because I didn’t modify collection before filtering, after when I filtered with Option1 I did mods to the collection because I wanted to filter based on displayed formatted date. Finally when I used Option3 I was only modifying collection for needs of current Option3 filter – without considering that before filtering I should consider mods that I have done during Option2 filtering. To confirm/test I have created a global array which holds my modified collection, so every time I’m filtering with any of the options I’m using that global array. It’s very dirty temporary solution but it works. Hope I clarified this problem enough. Is there a better solution?
It seems like you're trying to combine learning JavaScript and Angular at the same time, tough times!
module.filter('myCustomFilter', function ($filter) {
// You probably aren't trying to return "function", here
return function(items, searchedTxt, headers) {
if (headers.choice === "option1") {
return resultByDates(items, searchedTxt);
} else if (headers.choice === "option2") {
return resultByName(items, searchedTxt, headers);
}
else if (headers.choice === "option3") {
return resultSimple(items, searchedTxt);
}
return items;
};
// are you declaring another function after you've returned?
function resultByDates(items, search) {
if (search === undefined || search === null)
return items;
var k = Object.keys(search)[0];
var i;
for (i = 0; i < items.length; i++) {
items[i][k] = $filter('date')(items[i][k], "MM/dd/yyyy");
}
var filteredData = $filter('filter')(items, search);
var indexes = [];
for (i = 0; i < filteredData.length; i++) {
indexes.push(items.indexOf(filteredData[i]));
}
var output = [];
for (i = 0; i < indexes.length; i++) {
output.push(items[indexes[i]]);
}
return output;
}
// another??
function resultByName(items, search, headers) {
if (search === undefined || search === null)
return items;
if (headers !== undefined) {
var k = Object.keys(search)[0];
var i;
var componentVals = headers.componentVals;
var itemsCopy = angular.copy(items);
for (i = 0; i < items.length; i++) {
for (var obj in componentVals) {
if (componentVals[obj].ID === itemsCopy[i][k]) {
itemsCopy[i][k] = componentVals[obj].Name;
break;
}
}
}
var filteredData = $filter('filter')(itemsCopy, search);
var indexes = [];
for (i = 0; i < filteredData.length; i++) {
indexes.push(itemsCopy.indexOf(filteredData[i]));
}
var output = [];
for (i = 0; i < indexes.length; i++) {
output.push(items[indexes[i]]);
}
return output;
}
return items;
}
// christ!!
function resultSimple(items, search) {
if (search === undefined || search === null)
return items;
return $filter('filter')(items, search);
}
});
So, the first line, function($filter) {... is the function that is "invoked" when the list filter is triggered. The value that it returns will be the values that constitute the new list.
So if you "return" [1, 2, 3, 4] you will see those in the list in the browser. But if your function immediately returns a "function" instead of a "list" ([]), well then the browser will probably do something weird with it...
Oh wait, you're calling a bunch of functions that are outside of the current function you're in. Probably remove resultByDates from the current function call, as well as resultByName..., put them as siblings to module.filter.
This question is very hard to fix with so many issues, can you isolate a simpler problem? Maybe a small piece of unexpected behavior?

Infinite loop in list iterator

I created a list iterator but when trying traverse a list backward the loop runs infinitely. What I did wrong?
function List() {
this.listSize=0;
this.pos=0;
this.dataStore =[];
this.append = append;
this.currPos = currPos;
this.end = end;
this.front = front;
this.length = length;
this.moveTo = moveTo;
this.next = next;
this.prev = prev;
}
function append(element) {this.dataStore[this.listSize++]=element;}
function currPos() {return this.pos;}
function end() {this.pos = this.listSize-1;}
function front() {this.pos =0;}
function length() {return this.listSize;}
function moveTo(position) {this.pos = position;}
function prev() {if(this.pos > 0) --this.pos;}
function next() {if(this.pos < this.listSize) ++this.pos;}
var names = new List();
names.append("A"); names.append("B"); names.append("C");
for(names.end(); names.currPos() >= 0; names.prev()) {console.log(names.getElement());}
Your loop only terminates when the current list position is less than zero, but your .prev() function won't allow that to happen.
To fix it? Well, that's a matter of opinion, but if you're going to the trouble of implementing a list class you might as well make a native .forEach function:
function forEach(callback) {
for (var i = 0; i < this.listSize; ++i)
callback(this.dataStore[i], i);
}
Then you can do:
names.forEach(function(name) { console.log(name); });
I ran into a similar problem when trying to implement a list ADT from the book "Data Structures and Algorithms" and came to find out that the author re-wrote that section in later versions to look like this:
module.exports = List;
function List() {
this.listSize = 0;
this.pos = 0;
this.dataStore = [];
this.clear = clear;
this.find = find;
this.toString = toString;
this.insert = insert;
this.append = append;
this.remove = remove;
this.front = front;
this.end = end;
this.prev = prev;
this.next = next;
this.length = length;
this.currPos = currPos;
this.moveTo = moveTo;
this.getElement = getElement;
this.length = length;
this.contains = contains;
this.hasNext = hasNext;
this.hasPrevious = hasPrevious;
this.insertIf = insertIf;
}
function append(element) {
this.dataStore[this.listSize++] = element;
}
function find(element) {
for (var i = 0; i < this.dataStore.length; ++i) {
if (this.dataStore[i] === element) {
return i;
}
}
return -1;
}
function remove(element) {
var foundAt = this.find(element);
if (foundAt > -1) {
this.dataStore.splice(foundAt, 1);
--this.listSize;
return true;
}
return false;
}
function length() {
return this.listSize;
}
function toString() {
return this.dataStore;
}
function insert(element, after){
var insertPos = this.find(after);
if(insertPos > -1){
this.dataStore.splice(insertPos+1, 0, element);
++this.listSize;
return true;
}
return false;
}
function clear() {
delete this.dataStore;
this.dataStore = [];
this.listSize = this.pos = 0;
}
function contains(element) {
for (var i = 0; i < this.dataStore.length; ++i) {
if(this.dataStore[i] === element) {
return true;
}
}
return false;
}
function front() {
this.pos = 0;
}
function end() {
this.pos = this.listSize-1;
}
function prev() {
return this.dataStore[--this.pos];
}
function next(){
return this.dataStore[this.pos++];
}
function hasNext(){
if (this.pos > this.listSize -1) {
return false;
} else {
return true;
}
}
function hasPrevious() {
if(this.pos <= 0) {
return false;
} else {
return true;
}
}
function currPos() {
return this.pos;
}
function moveTo(position) {
this.pos = position;
}
function getElement() {
return this.dataStore[this.pos];
}
function insertIf(element) {
var greaterThan = true;
for(this.front(); this.hasNext(); ){
if(this.next() > element) {
greaterThan = false;
break;
}
}
console.log(element);
if(greaterThan){
this.append(element);
return true;
} else {
return false;
}
}
Your loops will then look like this:
for (list.front(); list.hasNext();) {
var listItem = list.next();
if(listItem instanceof Customer) {
console.log(listItem.name + ", " + listItem.movie);
} else {
console.log(listItem);
}
}
This has proven to be a much more reliable implementation so you may want to consider switching to this.
You need to change your for loop to:
for(names.end(); names.currPos() > 0; names.prev())

Categories