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())
Related
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
The task is to code simple framework in jQuery style, to solve this i've written code:
(function(window) {
window.smp=function smpSelector(selector) {
return new smpObj(selector);
}
function smpObj(selector) {
this.length = 0;
if (!selector ) {
this.el = [];
return this;
}
if(typeof selector == 'string'){
if(selector[0]==='#'){
this.el = [document.getElementById(selector.slice(1, selector.length))];
this.length = 1;
return this;
} else if(selector[0]==='.'){
this.el = document.getElementsByClassName(selector.slice(1, selector.length));
this.length = this.el.length;
return this;
} else {
this.el = document.getElementsByTagName(selector);
this.length = this.el.length;
return this;
}
}
else return null;
}
window.smp.changeColor=function smpColor(color) {
for (var i = 0; i < this.length; i++)
this.el[i].style.color = color;
}
})(window);
And it's working OK. I can call like:
smp('div')
But then i've tried to add method:
window.smp.changeColor=function smpColor(color) {
for (var i = 0; i < this.length; i++)
this.el[i].style.color = color;
}
And it's not working properly.
smp('div').changeColor('color')
(can't call like this )
I'll be grateful, if someone can show me mistake.
I've used this article article
Since you return this in smpObj function, this is the instance of smpObj not window.smp and there's no changeColor method for smpObj.
To make it work, you can do like this:
(function(window) {
window.smp = function smpSelector(selector) {
return new smpObj(selector);
};
function smpObj(selector) {
this.length = 0;
this.changeColor = function smpColor(color) {
for (var i = 0; i < this.length; i++) this.el[i].style.color = color;
};
if (!selector) {
this.el = [];
return this;
}
if (typeof selector == "string") {
if (selector[0] === "#") {
this.el = document.getElementById(selector.slice(1, selector.length));
this.length = 1;
return this;
} else if (selector[0] === ".") {
this.el = document.getElementsByClassName(
selector.slice(1, selector.length)
);
this.length = this.el.length;
return this;
} else {
this.el = document.getElementsByTagName(selector);
this.length = this.el.length;
return this;
}
} else return null;
}
})(window);
then try
smp('div').changeColor('red')
The following function returns val=ret instead of this. It is complicated and not clear:
getElement: function (nodeId) {
var ret = null;
if (nodeId === this._nodeId) {
ret = this;
} else {
for (var i = 0; i < this._selects.length; i++) {
ret = this._selects[i].getElement(nodeId);
if (ret) {
break;
}
}
}
return (ret);
},
Could you suggest an easier way for that? I tried the following, but you can't
do true/false with if(this._pages[i].getElement(nodeId):
getElement: function (nodeId) {
for (var i = 0; i < this._pages.length; i++) {
if(this._pages[i].getElement(nodeId){
return this;
}
}
return null;
},
I think you should return
this._pages[i].getElement(nodeId)
The second is not the same, because the first check is missing. (No need for an else part if a return is in the then part.)
getElement: function (nodeId) {
var i, ret;
if (nodeId === this._nodeId) {
return this;
}
for (i = 0; i < this._selects.length; i++) {
ret = this._selects[i].getElement(nodeId);
if (ret) {
return ret;
}
}
return null;
},
I have a create a list using javascript and at the end I have an iterator that goes through the list and displays the contents. Unfortunately my iterator goes into an infinite loop and I can't debug it.
This is the code -If you run the iterator your browser will crush - every other functions seems to work well.
function List(){
this.listSize = 0;
this.pos = 0;
this.dataStore = [];
this.clearList = 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;
}
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] == elemen){
return true;
}
}
return false
}
function front(){
this.pos = 0;
}
function end(){
this.pos = this.listSize-1;
}
function prev(){
if(this.pos > 0){
--this.pos;
}
}
function next(){
if(this.pos < this.listSize -1){
++this.pos;
}
}
function currPos(){
return this.pos;
}
function moveTo(position){
this.pos = position;
}
function getElement(){
return this.dataStore[this.pos];
}
var names = new List();
names.append("Clayton");
names.append("Raymond");
names.append("Cynthia");
names.append("Jennifer");
names.append("Bryan");
names.append("Danny");
names.front();
console.log(names.getElement());
names.next();
console.log(names.getElement());
names.next();
names.next();
names.prev();
console.log(names.getElement());
//iterator starts here
for(names.front(); names.currPos() < names.length(); names.next()){
console.log(names.getElement());
}
The for loop terminates when pos >= listSize. But next() will not increment pos unless pos < listSize - 1. Once pos == listSize - 1, next() will not increment pos. So the maximum value of pos is listSize - 1. Therefore the loop will never terminate.
findElementById
function findElementRecursion (inputId, currentElement) {
if (currentElement.id == inputId) {
return true;
}
var numChildren = currentElement.children.length;
if (numChildren > 0) {
for (i=0; i<numChildren; i++) {
var currentChild = currentElement.children[i];
console.log(currentChild);
return findElementRecursion(inputId, currentChild);
}
}
return false;
}
Would anyone help me debug this recursive function?
It returns false when it should return true. Since I don't see any error message, I am having a hard time trying to figure out what's the problem. It would be helpful if someone told me the steps of debugging this kind of problem.
update:
the problem was that I was returning only after inspecting the first child of currentElement. I fixed that problem but the function still returns false when it should return true.
function findElementRecursion (inputId, currentElement) {
if (currentElement.id == inputId) {
return true;
}
var numChildren = currentElement.children.length;
if (numChildren > 0) {
for (i=0; i<numChildren; i++) {
var currentChild = currentElement.children[i];
if (findElementRecursion(inputId, currentChild)) {
return true;
}
}
}
return false;
}
Try this from console on this page with findElementRecursion (mainbar, document) and you will see false.
function findElementRecursion (inputId, currentElement) {
var result = false;
if (currentElement.id == inputId) {
return true;
}
for (var i=0; i<currentElement.children.length; i++) {
var currentChild = currentElement.children[i];
console.log(currentChild);
result = findElementRecursion(inputId, currentChild);
// we can break here
if(result)
return true;
}
return false;
}
If I was going to do this, I wouldn't use a recursive function, I'd just use getElementsByTagName. It would be faster and traverse elements in the same order. It will also only visit elements, whereas other methods may visit text nodes too.
function findElementById(id, root) {
root = root || document;
var elements = root.getElementsByTagName('*');
for (var i=0, iLen=elements.length; i<iLen; i++) {
if (elements[i].id == id) {
return elements[i];
}
}
}
But maybe you just want a recursive function for the exercise.
function findElementByIdRecursive(id, element) {
// If no element passed in, use document
element = element || document;
// If element has id, return it
if (element.id == id) return element;
// Otherwise, keep going
var foundElement;
var childNodes = element.childNodes;
// Not needed on modern browsers but older browsers may throw an error
// if the node can't have children (like a text node)
if (childNodes) {
// Only keep looping while a matching element isn't found
for (var i=0,iLen=childNodes.length; i<iLen && !foundElement; i++) {
// Save a call if this node matches
if (childNodes[i].id == id) {
return childNodes[i];
}
// Otherwise, go down its children
foundElement = findElementByIdRecursive(id, childNodes[i]);
}
return foundElement;
}
}
There are probably better ways to do this - like using jQuery - but this should work:
function findElementRecursion (inputId, currentElement) {
if (currentElement.id == inputId) {
return true;
}
if (!currentElement.children) return false;
var numChildren = currentElement.children.length;
if (numChildren > 0) {
for (var i in currentElement.children) {
var currentChild = currentElement.children[i];
console.log(currentChild);
if (findElementRecursion(inputId, currentChild)) return true;
}
}
return false;
}
Notice that you also have to check if the element actually has children
you break the loop early:
function findElementRecursion (inputId, currentElement) {
var result = false;
if (currentElement.id == inputId) {
return true;
}
var numChildren = currentElement.children.length;
if (numChildren > 0) {
for (i=0; i<numChildren; i++) {
var currentChild = currentElement.children[i];
console.log(currentChild);
result = findElementRecursion(inputId, currentChild);
if(result){
break;
}
}
}
return result;
}
From the initial code,
function findElementRecursion (inputId, currentElement) {
if (currentElement.id == inputId) {
return true;
}
var numChildren = currentElement.children.length;
if (numChildren > 0) {
for (i=0; i<numChildren; i++) {
var currentChild = currentElement.children[i];
console.log(currentChild);
return findElementRecursion(inputId, currentChild);
}
}
return false;
}
The problem was I was returning too early and breaking out of the loop after only checking the first element of each currentElement.
function findElementRecursion (inputId, currentElement) {
if (currentElement.id == inputId) {
return true;
}
var numChildren = currentElement.children.length;
if (numChildren > 0) {
for (i=0; i<numChildren; i++) {
var currentChild = currentElement.children[i];
console.log(currentChild);
if (findElementRecursion(inputId, currentChild)) return true;
}
}
return false;
}
However, the function still returns false when it should return true. That's because I didn't set i as local variable. It should have been var i so it is contained in each local environment.
function findElementRecursion (inputId, currentElement) {
if (currentElement.id == inputId) {
return true;
}
var numChildren = currentElement.children.length;
if (numChildren > 0) {
for (var i=0; i<numChildren; i++) {
var currentChild = currentElement.children[i];
console.log(currentChild);
if (findElementRecursion(inputId, currentChild)) return true;
}
}
return false;
}
Everything is good!