JavaScript Throws Undefined Error - javascript

What it is supposed to do -
Example
url1(pages,"ALT") returns "www.xyz.ac.uk"
url1(pages,"xyz") returns ""
The error - TypeError: Cannot call method 'toUpperCase' of undefined
This is just for some coursework, Im stuck with these errors. Any help would be much appreciated
function index(string,pattern,caseSensitive) {
if(caseSensitive == false) {
var v = string.toUpperCase();
} else {
var v = string;
}
return indexNumber = v.indexOf(pattern);
}
var pages = [ "|www.lboro.ac.uk|Loughborough University offers degree programmes and world class research.", "!www.xyz.ac.uk!An alternative University" , "%www%Yet another University"];
alert(url1(pages, "ALT"));
function url1(pages,pattern) {
var siteContent = [];
for(i=0;i<pages.length;i++) {
var seperator = pages[i].charAt(0);
if(pages[i].indexOf(seperator)>0){
siteContent = pages[i].split(pages[i].indexOf(seperator));
}
if( index(siteContent[2],pattern,false)>=0){
return siteContent[1];
}else{
return "";
}
}
}

if(pages[i].indexOf(seperator)>0){
siteContent = pages[i].split(pages[i].indexOf(seperator));
}
if( index(siteContent[2],pattern,false)>=0){
return siteContent[1];
}else{
return "";
}
If pages[i].indexOf(seperator)<=0, siteContent is still whatever it was from the last iteration. If that happens on the first iteration, siteContent is still [], and siteContent[2] is undefined.
Another problem: the expression pages[i].indexOf(seperator) returns a number, and pages[i].split expects a delimiting string as an argument. Since the number doesn't appear in your input, you'll always get a single-element array, and siteContent[2] will always be undefined. Get rid of .indexOf(seperator), change it to siteContent = pages[i].split(seperator).
One more: get rid of the else { return ""; }. Add a return ""; after the for loop.
Finally, in the first if statement condition, change .indexOf(seperator) > 0 to .indexOf(seperator, 1) !== -1. Since you're getting seperator from the first character of the string, it will be found at 0. You want the second occurrence, so start the search at 1. In addition, .indexOf returns -1 if it doesn't find the substring. You'll need to account for this in both if conditions.
Side note, as this is not causing your problem: never use == false. JS will coerce stuff like 0 and "" to == false. If that's what you want, just use the ! operator, because the expression has nothing to do with the value false.
My final answer is http://jsfiddle.net/QF237/

Right here:
alert(url1(pages, ALT)); // ALT ISN'T DEFINED
I believe you forgot to quote it:
alert(url1(pages, "ALT"));

You should split the string passing the separator character itself. Your function then will look like:
function url1(pages,pattern) {
var siteContent = [];
for(i=0;i<pages.length;i++) {
var seperator = pages[i].charAt(0);
console.log(seperator);
if(pages[i].indexOf(seperator)>=0){
siteContent = pages[i].split(seperator); //fixed here
}
console.log(siteContent);
if( index(siteContent[2],pattern,false)>=0){
return siteContent[1];
}else{
return "";
}
}
}
Tell us if it worked, please.
EDIT: It seeems your index() also has a little problem. Please try the function below.
function index(string,pattern,caseSensitive) {
var v;
if(caseSensitive == false) {
v = string.toUpperCase();
pattern = pattern.toUpperCase(); //to clarify: pattern should be uppercased also if caseSensitiveness is false
} else {
v = string;
}
return v.indexOf(pattern);
}
EDIT 2:
And url1() is finally like this:
function url1(pages,pattern) {
var siteContent = [];
for(i=0;i<pages.length;i++) {
var seperator = pages[i].charAt(0);
if(pages[i].indexOf(seperator)>=0){
siteContent = pages[i].split(seperator);
}
if( index(siteContent[2],pattern,false)>=0){
return siteContent[1];
}
}
return "";
}
In this case, the first occurrence of pattern in all pages will be returned.

Related

Count each character in string using recursion

i have tried to make a function count each character in a string using recursion, for 2 days now. I tried to write some pseudo-code, but i can't really implement it.
Pseudocode:
write a function that takes text as a parameter
set a counter, for each element
set a result, using key,value for each character in element
base case: if we only have 1 string, then return the character and string
else return function-1 until the last element is hit.
var tekst = "We have to count strings";
function countStrings(tekst) {
var count = 0
var result = {}
if (count > tekst.lentgh) {
count++
return result
} else {
return countStrings(tekst-1)
}
}
console.log(countStrings(tekst))
Consider using this logic:
var tekst = "We have to count strings";
function countStrings(tekst) {
if (tekst.length == 0) {
return 0;
}
return 1 + countStrings(tekst.substring(1));
}
console.log(countStrings(tekst))
The approach here is, at each step of the recursion, to return 1 plus whatever the length of the substring from the next character onwards is. That is, we recurse down the input string, one character at a time, building out the length. The base case here occurs when the input to countStrings() happens to be empty string. In this case, we just return 0, and stop the recursive calls.
I decided to attempt this problem and this is what I came up with. Definitely a challenging problem so don't feel bad if you didn't get it:
var countStrings = function(tekst) {
if (tekst.length === 0) return {};
var obj = countStrings(tekst.slice(1));
if (obj[tekst[0]]) {
obj[tekst[0]] += 1;
} else {
obj[tekst[0]] = 1;
}
return obj;
};

Inefficient/ugly replacement function

I wrote a function that is supposed to replace code in between of two delimiters with the value, it returns (The string I'm applying this to is the .outerHTML of a HTML-Object).
This will be used similar to how it is used in e.g. Vue.js or Angular.
It looks like this:
static elemSyntaxParse(elem) {
let elem = _elem.outerHTML;
if (elem.includes("{{") || elem.includes("}}")) {
let out = "";
if ((elem.match(/{{/g) || []).length === (elem.match(/}}/g) || []).length) {
let occurs = elem.split("{{"),
key,
temp;
for (let i = 1; i < occurs.length; i++) {
if (occurs[i].includes("}}")) {
key = occurs[i].substring(0, occurs[i].indexOf("}}"));
temp = eval(key) + occurs[i].substring(occurs[i].indexOf("}}") + 2);
out += temp;
} else {
ModularCore.err("Insert-Delimiters \"{{\" and \"}}\" do not match.");
break;
return elem;
}
}
return occurs[0] + out;
} else {
ModularCore.err("Insert-Delimiters \"{{\" and \"}}\" do not match.");
return elem;
}
}
return elem;
}
(The function is inside of a class and refers to some external functions.)
Example use:
<body>
<p id="test">{{ Test }}</p>
<script>
let Test = 27;
document.getElementById("test").outerHTML = elemSyntaxParse(document.getElementById("test"));
</script>
</body>
Returns this string:
<p id="test">27</p>
It works but it is rather ugly and kinda slow.
How would I go about cleaning this up a bit? I am open to ES6.
PS: I now "eval() is evil" but this is the only occurrence in my code and it is (as far as i know) not replaceable in this situation.
Thanks!
I think you can omit a few checks and end up at:
const text = elem.outerHTML.split("{{");
let result = text.shift();
for(const part of text) {
const [key, rest, overflow] = part.split("}}");
if(!key || rest == undefined || overflow) {
ModularCore.err("Insert-Delimiters \"{{\" and \"}}\" do not match.");
return elem.outerHTML;
}
result += eval(key) + rest;
}
return result;
Invert the testing logic to get rid of nesting and else clauses
if (! elem.includes("{{") || !elem.includes("}}")) {
ModularCore.err("Insert-Delimiters \"{{\" and \"}}\" do not match.");
return elem;
}
// original loop code here
Don't double check - as #Bergi comment says.
test for return values indicating "not found, etc"
// if is removed. next line...
let occurs = elem.split("{{"), key, temp;
// if the separator is not in the string,
// it returns a one-element array with the original string in it.
if(occurs[0] === elem) return "no substring found";
The above should eliminate 2 nesting levels. You can then do a similar thing in that inner for loop.
Simplify compound logic.
!a || !b is equivalent to !(a && b). This is De Morgan's law

charAt not evaluating

I am trying to write a function that will evaluate equality of characters in a string and return true if 3 in a row match. The charAt() doesn't seem to be working as the if statement always goes to the else block.
function myFunction(num1)
{
var checkNum1;
for (var i = 0; i < num1.length; i++)
{
if (num1.charAt(i) == num1.charAt(i+1) && num1.charAt(i) == num1.charAt(i+2))
{
checkNum1 = true;
break;
}
}
if (checkNum1 == true)
{
return true;
}
else
{
return false;
}
}
What should I be doing to get the last "if" block to return true?
The code that you have provided works fine with strings ie: myFunction("257986555213") returns true.
However, myFunction(257986555213) returns false as expected as charAt is a String method.
As a fail-safe approach, you can try adding the following line to your method at the beginning:
num1 += '';
This should convert your argument to string and you should get your results..
Hope it Helps!!

Javascript Basic algorithm

Return true if the string in the first element of the array contains all of the letters of the string in the second element of the array. No case-sensitivity and order doesn't matter only the letters matter. For ex - ["Hello","hello"] returns true and so does ["Alien","lien"] and also ["Mary", "Aarmy"]. I think you get it. If not return false.
I could solve it with Array.indexOf() === -1 (in the first for loop) but can it work with this code, it's the opposite. I just can't make it return false. Ultimately, I wanna know, can you make it return false without changing the method.
function mutation(arr) {
var split = arr[1].toLowerCase().split("");
var splitSecond = arr[0].toLowerCase().split("");
for(k=0;k<=arr[0].length;k++){
for(i=0;i<=arr[1].length;i++){
if(split[i]===splitSecond[k]) {
return true
}
}
} return false
}
mutation(["hello", "hney"], "");
If using any other method, explain :)
The problem with your code is that you return true; as soon as one letter matches.
What you need to do is check if all letters match, which is easier achieved by checking if any letter doesn't match.
mainloop:
for(k=0;k<=arr[0].length;k++){
for(i=0;i<=arr[1].length;i++){
if(split[i]===splitSecond[k]) {
continue mainloop; // found the letter, move on to next search
}
}
return false; // failed to find letter, fail here
}
return true; // haven't failed yet and end of input reached. Success!
Another alternative would be:
for(k=0;k<arr[0].length;k++) {
if( arr[1].indexOf(split[k]) < 0) {
// letter not found
return false;
}
}
// not failed yet? Excellent!
return true;
function mutation(arr) {
var test = arr[0].toLowerCase(),
chars = arr[1].toLowerCase(),
len=chars.length;
for(var i=0;i<len;i++)
if(test.indexOf(chars[i])==-1) //this char not exist in test string
return false;
return true;//all chars already checked
}
mutation(["hello", "he"]);
https://jsfiddle.net/hb2rsm2x/115/
Here is an interesting way using regular expressions. escapeRegExp was taken from here.
function mutation(arr){
var matcher = new RegExp('[^'+escapeRegExp(arr[1])+']', "i");
return arr[0].match(matcher) === null;
}
function escapeRegExp(s) {
return s.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&')
}

Javascript if value is in array else in next array

I have found a few posts on here with similar questions but not entirely the same as what I am trying. I am currently using a simple if statement that checks the data the user enters then checks to see if it starts with a number of different values. I am doing this with the following:
var value = string;
var value = value.toLowerCase();
country = "NONE";
county = "NONE";
if (value.indexOf('ba1 ') == 0 || value.indexOf('ba2 ') == 0 || value.indexOf('ba3 ') == 0) { //CHECK AVON (MAINLAND UK) UK.AVON
country = "UK";
county = "UK.AVON";
} else if(value.indexOf('lu') == 0){//CHECK BEDFORDSHIRE (MAINLAND UK) UK.BEDS
country = "UK";
county = "UK.BEDS";
}
I have about 20-30 different if, else statements that are basically checking the post code entered and finding the county associated. However some of these if statements are incredibly long so I would like to store the values inside an array and then in the if statement simply check value.indexOf() for each of the array values.
So in the above example I would have an array as follows for the statement:
var avon = new Array('ba1 ','ba 2','ba3 ');
then inside the indexOf() use each value
Would this be possible with minimal script or am I going to need to make a function for this to work? I am ideally wanting to keep the array inside the if statement instead of querying for each array value.
You can use the some Array method (though you might need to shim it for legacy environments):
var value = string.toLowerCase(),
country = "NONE",
county = "NONE";
if (['ba1 ','ba 2','ba3 '].some(function(str) {
return value.slice(0, str.length) === str;
})) {
country = "UK";
county = "UK.AVON";
}
(using a more performant How to check if a string "StartsWith" another string? implementation also)
For an even shorter condition, you might also resort to regex (anchor and alternation):
if (/^ba(1 | 2|3 )/i.test(string)) { … }
No, it doesn’t exist, but you can make a function to do just that:
function containsAny(string, substrings) {
for(var i = 0; i < substrings.length; i++) {
if(string.indexOf(substrings[i]) !== -1) {
return true;
}
}
return false;
}
Alternatively, there’s a regular expression:
/ba[123] /.test(value)
My recomendation is to rethink your approach and use regular expressions instead of indexOf.
But if you really need it, you can use the following method:
function checkStart(value, acceptableStarts){
for (var i=0; i<acceptableStarts.length; i++) {
if (value.indexOf(acceptableStarts[i]) == 0) {
return true;
}
}
return false;
}
Your previous usage turns into:
if (checkStart(value, ['ba1', ba2 ', 'ba3'])) {
country = 'UK';
}
Even better you can generalize stuff, like this:
var countryPrefixes = {
'UK' : ['ba1','ba2 ', 'ba3'],
'FR' : ['fa2','fa2']
}
for (var key in countryPrefixes) {
if (checkStart(value, countryPrefixes[key]) {
country = key;
}
}
I'd forget using hard-coded logic for this, and just use data:
var countyMapping = {
'BA1': 'UK.AVON',
'BA2': 'UK.AVON',
'BA3': 'UK.AVON',
'LU': 'UK.BEDS',
...
};
Take successive characters off the right hand side of the postcode and do a trivial lookup in the table until you get a match. Four or so lines of code ought to do it:
function getCounty(str) {
while (str.length) {
var res = countyMapping[str];
if (res !== undefined) return res;
str = str.slice(0, -1);
}
}
I'd suggest normalising your strings first to ensure that the space between the two halves of the postcode is present and in the right place.
For extra bonus points, get the table out of a database so you don't have to modify your code when Scotland gets thrown out of leaves the UK ;-)

Categories